綜合演習 — 推理亂碼的成因
亂法自有指紋。透過模擬實務故障調查的 8 個案例,用全部章節的知識推理「這是用哪種編碼寫入的位元組序列,被當成哪種編碼讀取了」。
收尾的 8 題 — 亂掉的畫面是「證據」
最終章是綜合演習。前面的章節告訴我們,亂法自有指紋:排滿「縺」「繧」「繝」是 UTF-8 被 Shift_JIS 誤讀,排滿 ã 是被 Latin-1 誤讀,� 連發是 Shift_JIS 系被 UTF-8 誤讀,被替換成 ? 則是轉換時的資料喪失。亂掉的畫面不再是意義不明的符號,而是訴說著誤讀組合的證據。
8 個案例全部都請回到口訣 — 這是用哪種編碼寫入的位元組序列,被當成了哪種編碼來讀取?
| 卡住的時候 | 回去的章節 |
|---|---|
| 位元組序列與讀法、字元集與編碼的區別 | 第 1 章 |
| 碼位、U+XXXX 的讀法 | 第 2 章 |
| UTF-8 的位元組結構、E3 的意義、BOM | 第 3 章 |
| Shift_JIS 的 2 位元組結構、地雷字元、CP932 | 第 4 章 |
| 代理對、length 的數法 | 第 5 章 |
| 宣告與實體、Excel 與 BOM、DB 的連線設定 | 第 6 章 |
4 種經典模式。左上與右上是重新讀取就能復原的「誤讀」,右下是原始資料已喪失的「替換」— 分辨它們直接關係到能否復原的判斷。
支撐推理的 3 個事實(複習)
- 日文平假名的 UTF-8 以 E3 81 / E3 82 / E3 83 開頭(第 3 章)。這 2 個位元組在 Shift_JIS 的表中是「縺」「繧」「繝」— 所以 UTF-8 → Shift_JIS 誤讀時這 3 個字會大量出現。例如把日文的「文字化け」(亂碼)一詞本身拿去誤讀,會變成「譁�ュ怜喧縺�」
- Shift_JIS 的第 2 位元組侵入 ASCII 區域(第 4 章)。當成 UTF-8 讀時大半是非法位元組、變成 �(依環境也可能顯示成 ?),只有第 2 位元組的英文字母作為碎片殘留
- Latin-1 系是 1 位元組 = 1 字元,任何位元組都一定「讀得出來」。UTF-8 的 3 個位元組膨脹成 3 個拉丁字母,源自 E3 的 ã 是標記
那麼,開始吧。
案例 7-1 — 讀出亂法的指紋
範圍是第 1、3、4 章。亂掉的字元排列本身就是證據。
Q1. 案例:打開客戶寄來的 CSV,出現「縺薙s縺ォ縺。縺ッ」這種「縺」「繧」「繝」與半形片假名混雜的字串。最有力的推理是哪一個?
日文平假名的 UTF-8 位元組序列以 E3 81 / E3 82 / E3 83 開頭,把這 2 個位元組用 Shift_JIS 的表去查,分別會得到「縺」「繧」「繝」。所以把 UTF-8 的日文當成 Shift_JIS 誤讀時,這 3 個字會以異常的頻率出現,多出來的位元組看起來就是半形片假名(ォ、ッ 等)。反方向(把 Shift_JIS 當成 UTF-8 讀)如下一題所示會滿是替換字元,因此光看亂法的外觀就能連方向一起確定。
Q2. 案例:另一個檔案中,原本寫著日文「テスト」(測試)的地方變成了「�e�X�g」— 替換字元「�」之間零星殘留英文字母的形態。最有力的推理是哪一個?
「テスト」的 Shift_JIS 是 83 65 83 58 83 67。當成 UTF-8 讀時,0x83 是長得像「後續位元組」的孤立位元組,屬於非法而被替換成 �(U+FFFD)。另一方面,第 2 位元組的 65(e)、58(X)、67(g)落在 ASCII 範圍所以原樣倖存 — 第 4 章學過的「Shift_JIS 的第 2 位元組侵入 ASCII 區域」在這裡成了指紋。「� 連發 + 英文字母碎片」是把 Shift_JIS 系當成 UTF-8 讀的信號。
Q3. 案例:經過某個國外工具處理的日文文字,變成「ã“ã‚“ã«ã¡ã¯」這種帶重音符號的拉丁字母羅列,而且原本的 1 個字元膨脹成 3 個字元左右。最有力的推理是哪一個?
日文的 UTF-8 是 1 個字元 3 個位元組,開頭位元組幾乎都在 E3〜E9。在 Latin-1 系的表中,E3 對應 ã、E5 對應 å、E6 對應 æ,於是出現「每 3 個字元冒出一個 ã 之類的字母、字元數膨脹約 3 倍」的指紋(中間有些位元組會變成看不見的控制碼)。因為是用 1 位元組 = 1 字元的表在讀,不容易出現替換字元 — 這是與 q2 模式的分辨點。這是無視或遺失 charset 宣告的國外工具、郵件軟體的經典亂法。
案例 7-2 — 切分現場的故障
範圍是第 4〜6 章。CSV、郵件、替換 — 請從症狀的差異切分出原因所在的層。
Q4. 案例:關於自家系統輸出的 UTF-8(無 BOM)CSV,接到「用 Excel 雙擊開啟後日文變成『縺薙s…』般的亂碼」的詢問。原因與對策的正確組合是哪一個?
從「縺」的指紋可以推理出 UTF-8 被 Shift_JIS(CP932)系讀取,這與雙擊開啟的 Excel 的預設行為相符。在沒有宣告機制的 CSV 中,BOM(EF BB BF)是少數「可以埋進實體的標籤」,加上它是定石(第 6 章)。此外,這種情況下檔案的實體毫髮無傷,使用者端也可以改用匯入功能明確指定 UTF-8 來正確讀取。
Q5. 案例:遷移客戶名冊後,一部分客戶姓名變成了「????」。它與「縺薙s…」型亂碼的決定性差異是哪一個?
誤讀(縺薙s型)的位元組序列毫髮無傷所以可逆;替換成 ? 則是轉換處理把「表示不了的字元」改寫成 ? 這另一個字元後存檔的結果,不可逆。像 ① 或 髙 這類 CP932 的機種相依字元被丟進嚴格的轉換時就會發生(第 4 章)。處置也不同:誤讀就重新讀取,替換則需要從遷移來源的資料重新投入。能分清「亂碼」與「替換」,直接關係到能否復原的判斷。
Q6. 案例:收到的郵件內文正常,唯獨主旨變成「=?UTF-8?B?...?=」這種符號串或意義不明的文字。作為可能的原因,最貼切的是哪一個?
郵件的標頭在歷史上只允許 ASCII,所以非 ASCII 的主旨要包成「=?編碼名?B?Base64 資料?=」這種 MIME 編碼來傳送。主旨與內文的編碼宣告位置不同,只有一邊亂掉反而是很自然的症狀。接收端無法解讀 MIME、宣告與實體錯位、途中的系統弄壞了標頭等是典型原因 — 這是「宣告與實體」的思考方式(第 6 章)以標頭為單位適用的例子。
案例 7-3 — 推理的總收尾
橫跨全部章節。請回到口訣 — 這是用哪種編碼寫入的位元組序列,被當成哪種編碼讀取了。
Q7. 案例:把「ソフト一覧.csv」這份資料交給以 Shift_JIS 為前提的老介接程式,只有含日文片假名「ソ」的列會欄位錯位地被匯入。最有力的原因是哪一個?
「只有含特定字元的列會壞」正是第 4 章地雷字元的指紋。「ソ」(83 5C)的第 2 位元組與反斜線同值,以位元組為單位做跳脫處理的程式會把字元的中間誤認成符號。對策是改用能正確認識 Shift_JIS 的函式庫處理、把交接改成 UTF-8 等「正確處理字元分界」的方向。既不是片假名本身有錯,也不是溢位。
Q8. 案例:要把以 EUC-JP 累積的舊系統資料,遷移到 UTF-8 的新系統。正確的做法是哪一個?
這是課程的總整理。宣告只是標籤(第 6 章),實體的位元組序列要經過轉換處理才會改變。所謂轉換,就是「用 EUC-JP 的表讀取、還原成碼位(號碼),再用 UTF-8 的規則重編成位元組序列」— 一趟經由 2 個階段(第 1 章)的搬家。不是亂掉後再修,而是在跨越邊界之前正確轉換 — 讓「寫入的編碼」與「讀取的編碼」持續一致,就是字元編碼實務的全部。
課程的最後 — 帶著 2 個階段與 1 個提問上路
辛苦了。這個課程要你背下來的對應表,其實只有寥寥數行。取而代之希望你帶走的,是在任何場面都通用的觀點。
- 字元永遠以號碼(碼位)→ 位元組序列(編碼)的 2 個階段來處理
- 檔案的實體只有位元組序列。宣告(標籤)只是讀法的指示,不會轉換實體
- 亂掉時要問 — 這是用哪種編碼寫入的位元組序列,被當成了哪種編碼來讀取。亂法的指紋會告訴你方向
- 誤讀是可逆的,替換(?)是不可逆的。不要在亂掉的狀態下儲存
下次遇到亂碼時,你已經不是「隨便換個編碼重開看看的人」,而是能從證據鎖定誤讀組合、判斷該修宣告還是實體的人。第 3 章的模擬器今後仍可自由使用 — 遇到可疑的位元組序列時,請務必在手邊重現看看。