Как преобразовать эту строку в японский язык с помощью инструментов GNU/Linux?

Как преобразовать эту строку в японский язык с помощью инструментов GNU/Linux?

Вот строка из текстового файла:

@™TdaŽ®Æ‚êƒ~ƒNƒXƒgƒŒ[ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1³Ž®”z•z”Åj

Он содержит много непечатаемых символов и скопирован здесь:https://pastebin.com/TUG4agN4

С использованиемhttps://2cyr.com/decode/?lang=en, мы можем подтвердить, что это означает следующее:

 ☆Tda式照れミクストレート・ビキニver1.11d1.d2 (обновлено до версии 1.1)

Это при исходной кодировке = SJIS (shift-jis), отображается как Windows-1252.

Но как мы можем получить тот же результат без веб-сайта? Соответствующий инструмент — iconv, но что-то в цепочке инструментов сломалось. Если я попытаюсь выполнить cat из исходного текстового файла или использовать его как стандартный ввод с '<' в bash, один из 'iconv' в цепочке быстро выдаст ошибку. Если я скопирую указанную выше строку из текстового редактора gedit (читая файл как utf-16le) или как вывод iconv с преобразованием utf16-to-utf8, то результат будет близким, но все равно неверным:

@儺da式ニれミクストレ[トEビキニver1.11d1.d2iビキニモデルver.1.1ウ式配布版j

Некоторые свидетельства сбоя цепочки инструментов:

$ cat 'utf8.txt' |head -1

@™TdaŽ®Æ‚êƒ~ƒNƒXƒgƒŒ[ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1³Ž®”z•z”Å

$ cat 'utf8.txt' |head -1| iconv -f utf8 -t utf16

���@�"!Tda}��� ��~�N�X�g�R�[�g�E�r�L�jver1.11d1.d2�i�r�L�j� �f�9 ver.1.1��}� z" z ��j

Обратите внимание на три недопустимых символа в начале.

$ cat 'utf8.txt' |head -1| iconv -f utf8 -t utf16|iconv -f utf16 -t windows-1252

iconv: недопустимая последовательность ввода в позиции 2

$ echo "@™TdaŽ®Æ‚êƒ~ƒNƒXƒgƒŒ[ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1³Ž®”z•z”Åj"| значокv -f utf8 -t utf16

��@"!Tda}�� ��~�N�X�g�R[�gE�r�L�jver1.11d1.d2i�r�L�j� �f�9 ver.1.1�}� z" z �j

Обратите внимание на два недопустимых символа в начале, другие различия. Последовательность, скопированная из терминала, совпадает со строкой, отображаемой в текстовом редакторе, что подтверждается поиском (ctrl-F), сопоставляющим ее, что является той же строкой, которая дает правильный результат на 2cyr.com.

Расширение последней команды выше с помощью '|iconv -f utf16 -t windows-1252|iconv -f shift-jis -t utf8' дает близкий, но неверный результат, указанный выше, вместо того, чтобы выдать ошибку, как это происходит при прямой цепочке.

Если я попытаюсь создать файл с именем строки примера и применить к нему инструмент convmv, convmv скажет, что выходное имя файла содержит «символы, не соответствующие файловой системе POSIX! Это может привести к потере данных». Большинство имен файлов, недопустимых в UTF-8, не выдают этого предупреждения.

Есть ли какая-то последовательность битов, которую конвейеризация в bash не может обработать? Если нет, то почему цепочка инструментов не работает?

Видимо, разница в том, что bash не вставляет непечатаемые символы (поля с цифрами) в командную строку; может быть, 'readline' не может их обработать? Но близкий результат говорит о том, что порядок преобразования в цепочке инструментов правильный, так почему же он не работает?

Исходный файл, имя которого зашифровано другим способом (срок действия истекает через 30 дней):https://ufile.io/oorcq

решение1

Каналы — это функция ОС, которая работает с байтовыми буферами и никак не интерпретирует их содержимое. Поэтому переданный по каналу текст не проходит в bash иособенноникогда через 'readline'. Текст, вставленный как аргументы командной строки, пропускает. (И да, и readline, и терминал могут отфильтровывать управляющие символы в качестве меры безопасности.)

Ваш файл на самом деле представляет собой смесь двух кодировок, windows-1252и iso8859-1, из-за различных способов использования ими блока управляющих символов C1 (0x80..0x9F).

  • ISO 8859-1 использует весь этот диапазон для управляющих символов, а байты 0x80..0x9F соответствуют кодовым точкам Unicode U+0080..U+009F.
  • Windows-1252не могупредставляют собой управляющие символы C1; большую часть этого диапазона он использует для печатных символов и имеет несколько «дыр» – т. е. значений байтов, которым ничего не назначено (0x81, 0x8D, 0x8F, 0x90, 0x9D).
  • В остальном обе кодировки идентичны в диапазонах 0x00..0x7F и 0xA0..0xFF.

Давайте возьмем первую строку вашего «плохого» входного файла, декодированную из UTF-16 в текст Unicode и с экранированными непечатаемыми символами:

\u0081@\u0081™TdaŽ®\u008FÆ‚êƒ~ƒNƒXƒgƒŒ\u0081[ƒg\u0081EƒrƒLƒjver1.11d1.d2\u0081iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1\u0090³Ž®”z•z”Å\u0081j\n
  • Вы можете увидеть \u0081(U+0081), который соответствует байту 0x81 в ISO 8859-1, но не может быть закодирован в Windows-1252.
  • Вы также можете увидеть символ ƒ(U+0192), который соответствует 0x83 в Windows-1252, но вообще не существует в ISO 8859-1.

Поэтому трюк заключается в том, чтобы использовать Windows-1252, когда это возможно, и ISO 8859-1 в качестве запасного варианта, принимая решение индивидуально для каждой кодовой точки. (libiconv может сделать это через «ICONV_SET_FALLBACKS», но инструмент CLI iconvне может.) Легко написать свой собственный инструмент:

#!/usr/bin/env python3
with open("/dev/stdin", "rb") as infd:
    with open("/dev/stdout", "wb") as outfd:
        for rune in infd.read().decode("utf-16"):
            try:
                chr = rune.encode("windows-1252")
            except UnicodeEncodeError:
                chr = rune.encode("iso8859-1")
            outfd.write(chr)
            # outputs shift-jis

Обратите внимание, что толькополовинавашейвходной файлнеправильно закодирован Shift-JIS. Другая половина (английская) — это совершенно нормальный UTF-16; к счастью, Shift-JIS пропустит его, так что ручное разделение не требуется:

#!/usr/bin/env python3
with open("éΦé╟é▌üEé╓é╚é┐éσé▒éªéΦé⌐.txt", "r", encoding="utf-16") as infd:
    with open("りどみ・へなちょこえりか.txt", "w", encoding="utf-8") as outfd:
        buf = b""
        for rune in infd.read():
            try:
                buf += rune.encode("windows-1252")
            except UnicodeEncodeError:
                try:
                    buf += rune.encode("iso8859-1")
                except UnicodeEncodeError:
                    buf += rune.encode("shift-jis")
        outfd.write(buf.decode("shift-jis"))

Связанный контент