Base64 處理中文後出現亂碼,通常是字元與位元組之間的邊界出了問題。Base64 不知道輸入代表中文還是 emoji,它只儲存位元組。應用負責決定文字如何產生位元組,以及位元組怎樣還原成文字。
跟蹤位元組,而不只看文字
“中”的碼點是 U+4E2D,UTF-8 位元組為 E4 B8 AD,對應 Base64 為 5Lit。換一種字元編碼,位元組和 Base64 都可能改變。比較結果之前,應確認兩端都使用 UTF-8。
const bytes = new TextEncoder().encode('中');
console.log([...bytes]); // [228, 184, 173]
console.log([...bytes].map(b => b.toString(16)).join(' ')); // e4 b8 ad
'😊'.length 在 JavaScript 中是兩個 UTF-16 碼元,而它的 UTF-8 需要四個位元組。這些數值都不能直接當作任意文字的視覺字元數。傳輸和檔案計算應使用位元組長度。
讓替換錯誤可見
� 可能表示此前某一步已經丟失了資訊。未知位元組被替換成這個字元後,再次編碼通常無法恢復原值。繼續轉換前,應檢查原始解碼位元組。
const decoder = new TextDecoder('utf-8', { fatal: true });
console.log(decoder.decode(Uint8Array.of(0xe4, 0xb8, 0xad))); // 中
try { decoder.decode(Uint8Array.of(0xff)); }
catch { console.log('非法 UTF-8,應保留原始位元組。'); }
規範化是另外一種操作
外觀相同的文字可能由不同 Unicode 序列組成。例如 é 可以是預組合字元,也可以是 e 加組合重音。UTF-8 與 Base64 會正確儲存這種差異。如果應用需要統一形式,應在編碼前明確執行某種規範化,不能把改變位元組的操作稱為無損修復。
結果不同時,也應檢查不可見字元、換行風格和位元組序標記。它們都是真實輸入,對簽名和校驗和有影響。建議按“儲存原文 → 檢查位元組 → 確認字元編碼 → 明確處理解碼錯誤 → 考慮規範化”的順序排查。直接 btoa 處理 Unicode 的限制見 MDN: btoa();二進位制檔案則應完全繞過文本解碼。