1

Тема: AHK: Как перекодировать файл из UTF8 в ANSI

Добрый день!
Нужно перекодировать файл из utf8 в ansi. Т.е если я открываю файл в блокноте и сохраняю его в кодировке ANSI, то все хорошо. Как это можно сделать с помощью скрипта? Кто может подсказать алгоритм или готовую реализацию?
Спасибо!

2

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Я так понимаю, вам нужно вот это, но постараться поискать по форуму, вы не пытались ?
http://forum.script-coding.com/viewtopic.php?id=1179

Передумал переделывать мир. Пашет и так, ну и ладно. Сделаю лучше свой !

3

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Вот так попробуйте. По умолчанию здесь для ANSI используется кодировка 1251, но можно указать и явно при вызове функции Utf8ToAnsi, вторым параметром.

FileRead, Src, C:\Prog\Temp\utf8.txt  ; Читаем Utf8.

Ansi := Utf8ToAnsi(Src)  ; Преобразуем в Ansi.

FileAppend, %Ansi%, C:\Prog\Temp\ansi.txt  ; Сохраняем.


; ---- Функция преобразования ----

Utf8ToAnsi(ByRef Utf8String, CodePage = 1251)
{
    If (NumGet(Utf8String) & 0xFFFFFF) = 0xBFBBEF
        BOM = 3
    Else
        BOM = 0

    UniSize := DllCall("MultiByteToWideChar", "UInt", 65001, "UInt", 0
                                            , "UInt", &Utf8String + BOM, "Int", -1
                                            , "Int", 0, "Int", 0)
    VarSetCapacity(UniBuf, UniSize * 2)
    DllCall("MultiByteToWideChar", "UInt", 65001, "UInt", 0
                                 , "UInt", &Utf8String + BOM, "Int", -1
                                 , "UInt", &UniBuf, "Int", UniSize)

    AnsiSize := DllCall("WideCharToMultiByte", "UInt", CodePage, "UInt", 0
                                             , "UInt", &UniBuf, "Int", -1
                                             , "Int", 0, "Int", 0
                                             , "Int", 0, "Int", 0)
    VarSetCapacity(AnsiString, AnsiSize)
    DllCall("WideCharToMultiByte", "UInt", CodePage, "UInt", 0
                                 , "UInt", &UniBuf, "Int", -1
                                 , "Str", AnsiString, "Int", AnsiSize
                                 , "Int", 0, "Int", 0)
    Return AnsiString
}

4

Re: AHK: Как перекодировать файл из UTF8 в ANSI

YMP, Спасибо огромное за ответ, это то что надо.

5

Re: AHK: Как перекодировать файл из UTF8 в ANSI

YMP, не совсем ясен один момент. UniSize здесь — "the required buffer size" — требуемый размер буфера. Что здесь имеется ввиду? Ведь для UniBuf выделяется размер, в два раза больший:

VarSetCapacity(UniBuf, UniSize * 2)

?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Это размер в символах (двухбайтных). В старом SDK это более чётко указано, т.к. присутствует слово "wide":

If the function succeeds, and cchWideChar is zero, the return value is the required size, in wide characters, for a buffer that can receive the translated string.

Вот пример:

Ansi = Привет
UniSize := DllCall("MultiByteToWideChar", "uint", 0, "int", 0
                                        , "str", Ansi, "int", -1
                                        , "uint", 0, "uint", 0)
MsgBox, %UniSize%

Все юникодовские (точнее, UTF-16) размеры в этих функциях передаются и возвращаются в символах, а неюникодовские и UTF-8 — в байтах.

7

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Да, я обратил внимание, что в случае конвертирования из ANSI в Юникод этот размер соответствует длине строки в ANSI плюс 1 символ на завершающий 0. Спасибо, теперь прояснилось.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Вообще-то, смотря какая кодировка. Говорят, дальневосточные ANSI многобайтные и там соотношение размеров сложнее. Поэтому и требуется такой предварительный вызов функции, чтобы узнать реальный размер выходного буфера. На случай, если вдруг такая кодировка.

Завершающий нуль в возвращённом размере будет учтён, если передана -1 в качестве размера входной строки. Это подразумевает, что строка нуль-терминирована и ноль тоже нужен. Если указать не -1, а конкретное количество символов/байтов (возможно, не всю строку), то возвращённый размер выходного буфера расчитан строго под эти символы и соответственно нуль в конец при конвертации не будет добавлен.

9

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Спасибо, понял.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10

Re: AHK: Как перекодировать файл из UTF8 в ANSI

А как сделать с помощью этого кода конвертацию UTF-8 символов в ANSI?
Так не оплучается:

msgbox % Ansi := Utf8ToAnsi("Ä")  ; Преобразуем в Ansi.


; ---- Функция преобразования ----

Utf8ToAnsi(ByRef Utf8String, CodePage = 1251)
{
    If (NumGet(Utf8String) & 0xFFFFFF) = 0xBFBBEF
        BOM = 3
    Else
        BOM = 0

    UniSize := DllCall("MultiByteToWideChar", "UInt", 65001, "UInt", 0
                                            , "UInt", &Utf8String + BOM, "Int", -1
                                            , "Int", 0, "Int", 0)
    VarSetCapacity(UniBuf, UniSize * 2)
    DllCall("MultiByteToWideChar", "UInt", 65001, "UInt", 0
                                 , "UInt", &Utf8String + BOM, "Int", -1
                                 , "UInt", &UniBuf, "Int", UniSize)

    AnsiSize := DllCall("WideCharToMultiByte", "UInt", CodePage, "UInt", 0
                                             , "UInt", &UniBuf, "Int", -1
                                             , "Int", 0, "Int", 0
                                             , "Int", 0, "Int", 0)
    VarSetCapacity(AnsiString, AnsiSize)
    DllCall("WideCharToMultiByte", "UInt", CodePage, "UInt", 0
                                 , "UInt", &UniBuf, "Int", -1
                                 , "Str", AnsiString, "Int", AnsiSize
                                 , "Int", 0, "Int", 0)
    Return AnsiString
}

11

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Сейчас этот способ уже не актуален, для перекодировки можно использовать StrPut/StrGet.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Не пойму как использовать. Подскажешь? А то сейчас приходится через временный файл кодировать.

str := "Ä"
MsgBox, % Utf2Ansi(str)

Utf2Ansi(str)
{
   FileOpen("utf8", "w", "CP0").Write(str)
   FileRead, ansi, utf8
   FileDelete, utf8
   Return, ansi
}

13

Re: AHK: Как перекодировать файл из UTF8 в ANSI

А где у вас перекодировка? Вместо одного символа получается совсем другой.
В переменной str не utf-8, а utf-16. Можете конкретнее описать ситуацию, где вам требуется перекодировать?

14

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Мне надо чтобы получился результат идентичным сохранению в блокноте не ANSI символов, как ANSI.
То есть копируем этот символ в блокнот, сохраняем документ как ANSI.
Notepad сделает предупреждение, что не все символы сохранятся - мы согласимся с этим.
Закроем этот документ и откроем снова.
В результате "Ä" переконвертится в "A".

15

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Со StrPut/StrGet не выходит, заменяется на "?". Вариант через файл вполне годится. Этот файл даже на диск-то записаться не успеет, я думаю, т.е. все операции в памяти проходят. Только желательно единообразие. А то выглядит каким-то винегретом. Сначала объект используете, потом просто команды.

16

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Так разве лучше? Все-равно файл потом как-то удалять надо.
Жалко, что нельзя вообще без создания файла сделать.

str := "Ä"
MsgBox, % Utf2Ansi(str)

Utf2Ansi(str)
{
   File := FileOpen("utf8", "w", "CP0")
   File.Write(str)
   File.Close()
   File := FileOpen("utf8", "r-d", "CP0")
   ansi := File.Read()
   File.Close()
   Return, ansi
}

17

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Два раза можно не открывать.


str := "Ä"
MsgBox, % Utf2Ansi(str)

Utf2Ansi(str)
{
   File := FileOpen("utf8", "rw", "CP0")
   File.Write(str)
   File.Pos := 0
   ansi := File.Read()
   File.Close()
   FileDelete, utf8
   Return, ansi
}

А чем вам создание файла конкретно мешает? Я так понимаю, это религия? Какая-то идиосинкразия к временным файлам у народа.

Без файла можно, но тогда больше писанины.


str := "Ä"
MsgBox, % ToAnsi(str)

ToAnsi(str)
{
    len := StrLen(str)
    VarSetCapacity(buf, len)
    prev_locale := StrGet( DllCall("msvcrt.dll\_wsetlocale", "int", 0, "ptr", 0, "cdecl") )
    DllCall("msvcrt.dll\_wsetlocale", "int", 0, "str", "Russian", "cdecl")
    DllCall("msvcrt.dll\wcstombs", "ptr", &buf, "str", str, "ptr", len, "cdecl")
    DllCall("msvcrt.dll\_wsetlocale", "int", 0, "str", prev_locale, "cdecl")
    Return StrGet(&buf, len, "cp0")
}

18

Re: AHK: Как перекодировать файл из UTF8 в ANSI

YMP, спасибо!

А чем вам создание файла конкретно мешает? Я так понимаю, это религия?

Просто этих обработок очень много - из-за этого притормаживает и не хочется ресурс SSD диска рассходовать.

19

Re: AHK: Как перекодировать файл из UTF8 в ANSI

А как одним махом можно преобразовать ISO 8859-1 Latin 1; Western European (ISO) в Ansi?
У меня только через 2 преобразования получается:

var := "ó"
Utf8ToLatin(var)
MsgBox, % ToAnsi(var)

Utf8ToLatin(ByRef string) {
   VarSetCapacity(latin, StrPut(string, "cp28591"))
   StrPut(string, &latin, "cp28591")
   string := StrGet(&latin, "UTF-8")
}

ToAnsi(str)
{
    len := StrLen(str)
    VarSetCapacity(buf, len)
    prev_locale := StrGet( DllCall("msvcrt.dll\_wsetlocale", "int", 0, "ptr", 0, "cdecl") )
    DllCall("msvcrt.dll\_wsetlocale", "int", 0, "str", "Russian", "cdecl")
    DllCall("msvcrt.dll\wcstombs", "ptr", &buf, "str", str, "ptr", len, "cdecl")
    DllCall("msvcrt.dll\_wsetlocale", "int", 0, "str", prev_locale, "cdecl")
    Return StrGet(&buf, len, "cp0")
}

20

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Одним никак, наверно, только через посредство какого-нибудь Юникода. Кстати, Latin-1 — это тоже ANSI, только не наша.

21

Re: AHK: Как перекодировать файл из UTF8 в ANSI

А вот такие символы возможно заменить иным методом, кроме как через RegexReplace?

msgbox % replace("ÐæẔ")

replace(data)
{
   list:={ầẦắẮằẰẵẴảẢạẠậẬấẤ:"a",æÆǣǢ:"ae",ßþÞ:"b",ḑḐðÐḍḌḑḐ:"d",ếẾềỀệỆǝƎəƏểỂễỄẹ̀Ẹ̀ɛ́ɛƐ:"e",ḩḨḥḤẖ̱̲̲ḩḨ:"h",ııỉỈịỊ:"i",ŋŊ:"n",ốỐồỒọỌớỚộỘɔƆɔ́Ɔ́ổỔỏỎ:"o",œŒœ̆Œ̆:"oe",ṣṢș̲̲:"s",ṭṬț:"t",ủỦừỪựỰụỤṳṲứỨ:"u",ỹỸỳỲ̧̧̄̄:"y",ẕẔ:"z"}
   for k,v in list
      data := RegexReplace(data, "[" k "]", v)
   return data
}

22

Re: AHK: Как перекодировать файл из UTF8 в ANSI

Мне такой метод не известен.