Base64 および UTF-8: 中国語のテキスト、絵文字、置換文字を診断します。

Unicode および UTF-8 バイトを通じてテキストをトレースし、デコード エラーと正規化を区別し、無効なバイナリ入力を保存します。

Base64 が「中国語を壊している」ように見える場合、問題は通常、文字とバイトの境界にあります。 Base64 は、シーケンスが中国語のテキストまたは絵文字を表すことを認識しません。バイトを保持します。アプリケーションは、それらのバイトがどのように生成され、解釈されるかを選択します。

バイトをフォローする

「中」という文字は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

絵文字は別の違いを示しています: JavaScript '😊'.length は 2 つの UTF-16 コード単位ですが、その UTF-8 表現は 4 バイトです。どちらの数値も単独では、任意のテキスト内でユーザーが認識できる表示文字の数ではありません。ファイルとトランスポートの計算にはバイト長を使用します。

置換を可視化する

置換文字 � は、以前のデコーダがすでに情報を失ったことを意味している可能性があります。文字列内の未知のバイトが置き換えられ、その文字列が再エンコードされると、通常、元の値を再構築することはできません。さらに変換を行う前に、生のデコードされたバイト配列を検査してください。

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('Invalid UTF-8; preserve the original byte.'); }

正規化は別の操作です

2 つの文字列は、異なる Unicode シーケンスを含んでいるにもかかわらず、同一に見えることがあります。アクセント付き文字 é は、事前に作成された 1 つの文字か、その後に結合アクセントが続く e の場合があります。 UTF-8 と Base64 はその違いを正しく保存します。アプリケーションで正規化されたテキストが必要な場合は、正規化形式を明示的に選択し、エンコード前にそれを適用します。変更された結果をバイト保存修復とは決して呼ばないでください。

結果が異なる場合は、非表示の文字、行末、およびバイト オーダー マークの可能性も確認してください。これらは実際の入力バイトであり、必ずしもエンコーダのバグではありません。署名付きメッセージとチェックサムの場合、目に見えない違いによっても結果が変わります。

信頼できるトラブルシューティングの順序は、ソースを保存し、バイトを検査し、予期される文字エンコーディングを特定し、明示的なエラー処理でデコードしてから、正規化を検討することです。 MDN: btoa() ブラウザの直接 btoa 呼び出しが任意の Unicode テキストに適さない理由を説明します。バイナリ ファイルはテキスト デコードを完全にバイパスする必要があります。