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

485
Misaki

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

@ ™ TdaŽ®Æ,êƒ ~ ƒNƒXƒgƒŒ [ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ,ƒfƒ <ver.1.1³Ž®»г • г» Aj

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

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

☆ Tda 式 照 れ ミ ク ス ト レ ー ト · ビ キ ニ ver1.11d1.d2 (ビ キ ニ モ デ ル Ver.1.1 正式 配 布 版)

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

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

@ 儺 да 式 ニ れ ミ ク ス ト レ [ト 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³Ž®»г • г» Å

$ 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 Ver9 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 "| iconv -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

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

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

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

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

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

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

2

1 ответ на вопрос

3
grawity

Каналы - это функция ОС, которая работает с байтовыми буферами и никак не интерпретирует их содержимое. Таким образом, переданный по конвейеру текст не проходит через 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', но iconvинструмент CLI не может.) Легко написать свой собственный инструмент:

#!/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")) 
Это хорошее решение, которое отвечает на вопрос о том, как получить исходный текст. У меня такие вопросы: Misaki 6 лет назад 0
1) есть ли способ прочитать исходный файл, который не предусматривает откат ко второй кодировке? Мое предположение, что UTF-16 задействован, отчасти потому, что я пытался открыть его как другие кодировки в gedit, и все они потерпели неудачу. 2) Всегда ли работает этот метод чтения и преобразования одного символа / "руны" одновременно? Могут ли 2-байтовые символы быть неправильно декодированы как 3-байтовые или 1-байтовые символы, что приведет к «руне» со слишком большим или слишком маленьким объемом информации? Misaki 6 лет назад 0
3) 2cyr.com вынужден использовать тот же запасной вариант? Насколько я понимаю, строка отправляется в UTF-8, и при выборе настроек декодирования не упоминается ни UTF-16, ни ISO 8859-1. Кажется, довольно просто протестировать пары кодировок, таких как SJIS + Windows-1252, но обнаружение того, что UTF-16 также задействован, увеличивает сложность, и мое понимание недостаточно, что я не совсем уверен, что это нужно делать. Misaki 6 лет назад 0
Некоторые из этих комментариев могут быть посторонними и могут быть удалены. Я не думаю, что это совпадение, что отсутствующий символ в Windows-1252, 0x81, это U + 0081. Я думаю, что текстовый редактор, который первоначально считывал файл SJIS как Windows-1252, видел 0x81, не смог преобразовать его, а затем просто передал его. Затем 2cyr сделал то же самое при конвертации из Unicode (любого типа) в Windows-1252.Я предполагаю, что U + 0081 на самом деле нехорошо, это 0x0081 в UTF-16. Таким образом, вместо того, чтобы использовать в качестве альтернативы второе кодирование, это была бы необработанная битовая последовательность. Возможно sub-255 предполагается чистым программами. Misaki 6 лет назад 0
Или, поскольку U + 0081 в UTF8 равно 0xC2 0x81, резервной битовой последовательностью будет кодовая точка Unicode. Misaki 6 лет назад 0
@Misaki: 1) Да, задействован UTF-16 (ваш файл на 100% UTF-16), но даже после декодирования UTF-16 первая половина содержит бессмысленные данные, и это преобразование неизбежно. 2) Это работает _ как показано_ - каждая руна / кодовая точка Unicode будет сопоставляться с чем-то полезным; в вашем входном файле 100% из них могут быть сопоставлены с одним байтом каждый. Но вы также правы, что он не будет отображаться в последовательность _whole_ Shift-JIS, поэтому мой пример ждет до конца, чтобы окончательно декодировать весь буфер как Shift-JIS. Немедленное использование `rune.encode (" windows-1252 "). Decode (" shift-jis ")` очень быстро завершится неудачей. grawity 6 лет назад 0
@Misaki: 3) Я бы предположил, что это так. «Если не получится, попробуйте ISO 8859-1» - довольно распространенный подход. И UTF-16 больше не участвует, когда вы отправляете текст на 2cyr.com - ваш текстовый редактор уже расшифровал UTF-16 для вас. Браузер кодирует отправленный текст в UTF-8, а сервер декодирует его, но это прозрачная деталь. grawity 6 лет назад 0
@Misaki: Что касается того, как файл был изначально создан, «увидел 0x81, не смог преобразовать его, а затем просто передал его» - это может быть правдой, но это также может быть истолковано как отступление к ISO 8859-1, где 0x81 действительно отображается в U + 0081. (Как я уже сказал, этот тип отступления очень распространен ...) grawity 6 лет назад 0

Похожие вопросы