如何使用 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(ビキキモデルver.1.1.d2(ビキキモデルver.1.111正式配版)

這是來源編碼 = SJIS (shift-jis),顯示為 Windows-1252。

但如果沒有網站,我們要如何才能得到相同的結果呢?相關工具是 iconv,但工具鏈中的某些內容已損壞。如果我嘗試從來源文字檔案中進行 cat 操作或將其用作 bash 中帶有“<”的標準輸入,則鏈中的“iconv”之一很快就會出錯。如果我從文字編輯器 gedit 複製上述字串(將檔案讀取為 utf-16le)或透過 iconv 進行 utf16 到 utf8 轉換的輸出,則結果很接近,但仍然錯誤:

@儺da式ニれミクsutore[toEビキniver1.11d1.d2iビキキモデルver.1.1ウ式配布版j

工具鏈失敗的一些證據:

$ cat 'utf8.txt' |head -1

@™TdaŽ®Æ‚ê▪~▪N▪X▪g▪▪[▪gE▪▪L▪jver1.11d1.d2i▪▪▪▪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▪▪▪L▪jver1.11d1.d2i▪▪▪▪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 無效的檔案名稱不會發出此警告。

是否存在 bash 中的管道無法處理的位元序列?如果沒有,為什麼工具鏈不起作用?

顯然,差異是因為 bash 不會將未列印的字元(帶有數字的框)貼到命令列;也許“readline”無法處理它們?但結果很接近表明工具鏈中的轉換順序是正確的,那麼為什麼它不起作用呢?

原始文件,其文件名以不同方式加擾(30 天後過期):https://ufile.io/oorcq

答案1

管道是一項作業系統功能,它與位元組緩衝區一起使用,並且不會以任何方式解釋其內容。所以管道文本不會通過 bash 且尤其從來沒有通過「readline」。作為命令列參數貼上的文字也是如此。 (是的,readline 和終端都可以過濾掉控製字元作為安全措施。)

您的檔案實際上是兩種編碼的混合,windows-1252iso8859-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),它會對應到 ISO 8859-1 中的位元組 0x81,但無法在 Windows-1252 中進行編碼。
  • 您也可以看到符號ƒ(U+0192),它在 Windows-1252 中對應到 0x83,但在 ISO 8859-1 中根本不存在。

因此,訣竅是盡可能使用 Windows-1252,並使用 ISO 8859-1 作為後備,為每個代碼點單獨決定。 (libiconv 可以透過「ICONV_SET_FALLBACKS」來完成此操作,但 CLIiconv工具不能。)編寫自己的工具很容易:

#!/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"))

相關內容