開発者ツール · Base64 エンコーダーおよびデコーダー
mojibake を使用せずに Base64 を UTF-8 にデコードする: atob と TextDecoder
· 仕組み
base64 エンコード ユニコード
atob() は文字として偽装されたバイトを返すため、アクセント付きのテキストがデコード後に壊れて見えるのはこのためです。この投稿では、Base64 からバイト、UTF-8 テキストへの正しいパイプラインと、失敗パターンを認識する方法を示します。
「Café」にデコードされる API 応答 — 具体的な文字化けの症状と 2 つの間違った文字の後ろの 2 バイト
Base64 文字列 Q2Fmw6k= は、UTF-8 テキスト カフェであるバイト (67、97、102、195、169) にデコードされます。単純なデコーダ (atob と文字列変換だけ) に貼り付けると、出力は多くの場合 Café となり、各アクセントが 2 つの間違った文字に置き換えられます。この文字化けは、atob が UTF-8 テキストではなく、バイト文字列 (コード単位 0 ~ 255) を返すために発生します。バイト 195 および 169 は、UTF-8 のアクセント付き é をエンコードします。
それらを別の Latin-1 文字であるかのように扱うと、文字化けパターンが得られます。正しいパイプラインは atob (バイトを文字列として)、次に TextDecoder (バイトを UTF-8 として解釈) で、元のテキストが返されます。 atob 関数は壊れていません。バイナリデータ用に設計されています。その名前は ASCII からバイナリへの変換に由来しており、生成されるバイナリ文字列はコード単位 0 ~ 255 のシーケンスであり、それぞれが 1 バイトを表します。
atob() が実際に返すもの — デコードされたテキストではなくバイトを表すコード単位の文字列 0–255
Q2Fmw6k= (標準 Base64) を与えると、各文字が 1 バイトである文字列が出力されます: コード単位 67、次に 97、次に 102、次に 195、次に 169。その文字列を直接表示するか、Latin-1 テキストとして解釈すると、出力が文字化けします。欠けているステップは、コード単位をバイト配列に変換し、配列を UTF-8 としてデコードすることです。
charCodeAt ループはバイト値を回復します。atob 出力の文字ごとに、charCodeAt を呼び出してコード単位 (番号 0–255) を取得し、Uint8Array に保存します。バイト配列が存在したら、文字セット utf-8 を使用して TextDecoder に渡します。 TextDecoder はバイト シーケンスを読み取り、UTF-8 テキストとして解釈し、(195、169) のようなバイト シーケンスを é のような単一文字に結合します。バイト (67、97、102、195、169) は 4 文字の文字列 Café になります。このループは意図的に退屈です。返された各コード単位を charCodeAt で読み取り、それを一致する Uint8Array 位置に割り当てます。そこでは文字セットの決定は行われません。唯一の解釈は、TextDecoder がその配列を受け取り、致命的なエラー処理を伴う UTF-8 を適用するときに到着します。
文字列を Uint8Array に変換する — charCodeAt ループと、それが変換ではなくバイト コピーである理由
この 2 段階のプロセス (バイト回復、次に UTF-8 解釈) は、Base64 エンコーダーとデコーダーが内部で行うことです。 mojibake パターンは、このエラーの明らかな兆候です。 Café が Café として表示される場合は、UTF-8 バイトのラテン語 1 解釈が表示されます。 é の UTF-8 バイトは 0xC3 0xA9 (10 進数の 195、169) です。 Latin-1 では、コード単位 195 は Ã、コード単位 169 は © です。
UTF-8 バイト シーケンスが、各バイトが個別の Latin-1 文字であるかのように読み取られると、すべてのマルチバイト UTF-8 シーケンスで誤った置換文字が生成されます。 Café が Caf の後に置換文字が続く場合、または Caf として表示される場合は?または Caf と U+FFFD の場合、別のエラーが表示されます。デコーダはバイト シーケンスを有効な UTF-8 として認識しませんでした。具体的な例: Base64 SGVsbG8sIOS4lueVjCEg8J-Zgg== は次のようにデコードされます。
TextDecoder と文字セットの決定 — UTF-8 としてデコードすること、および文字セットが別個の事実である理由を知っておく必要がある
atob は、バイト (72、101、108、108、111、44、32、228、 184、180、149、140、33、32、240、159、152、 130)。最初の 6 バイトは ASCII です: Hello, になります。バイト 228、184、180 は、CJK 文字を表す 3 バイトの UTF-8 シーケンスです。バイト 149、140 は次のシーケンスの一部です。完全なシーケンスには、最後の文字の 4 バイトの絵文字シーケンス (240、159、152、130) が含まれます。
TextDecoder を通じて正しく処理されると、すべてのバイトが結合されて元の混合スクリプト テキストが生成されます。 UTF-8 バイト シーケンスの長さは予測可能です。0xxxxxxx で始まるバイトはシングルバイト ASCII です。 110xxxxx で始まるバイトは、10xxxxxx で始まる次のバイト (合計 2 バイト) を予期します。 1110xxxx で始まるバイトには、後続の 2 バイト (合計 3 バイト) が必要です。 11110xxx で始まるバイトには、後続の 3 バイト (合計 4 バイト) が必要です。 CJK と絵文字が混在したサンプルの場合、ASCII の直感は役に立たないため、バイト ビューが特に診断に役立ちます。表示される各シンボルには数バイトが属しており、1 バイトの削除により残りのシーケンスが無効な UTF-8 にシフトされます。厳密にデコードすると、それは一見もっともらしい損傷ではなく、名前付きの障害に変わります。
作業例: CJK と絵文字を含む Base64 文字列のデコード - バイト、コード ポイント、および元の文字列と比較した最終文字列
1111110x で始まるシーケンスは UTF-8 では無効です (将来のために予約されており、使用されません)。 10xxxxxx で始まるバイトが先頭バイトとして表示されることはありません。それは継続です。バイト ストリームがルールに違反している場合、それは無効です UTF-8。文字セット utf-8 を持つ TextDecoder は、これらのルールに従って配列を解釈し、有効なシーケンスの場合は成功します。無効な場合はエラーが報告されます。
Base64 エンコーダーおよびデコーダーは、厳密モード フラグが true の TextDecoder を使用します。これは、無効な UTF-8 によって置換文字 (U+FFFD) が黙って挿入されるのではなく、エラーが発生することを意味します。 Base64 文字列が有効な UTF-8 ではないバイトにデコードされる場合、文字化けしたテキストを続行する代わりに厳密モードがスローされます。これは設計上の選択です。バイナリ ペイロード (画像、キー、圧縮データ) はテキストではないため、テキストとしてデコードすべきではありません。
パターンの認識: Ã、â および � — Base64 の問題と文字セットの問題を区別する方法
JPEG を Base64 としてデコードしようとすると、バイト ストリームは有効な UTF-8 を表さず、厳密なデコードでは拒否されます。ツールは、そのようなペイロードの 16 進ビューを提供します。テキストであるかのように見せることなく、生のバイトを表示できます。 UTF-8 デコード エラーを認識するには、コンテキスト内のバイトを調べる必要があります。マルチバイトシーケンスが必要な場合、それらは奇数ですか?
潜在的なシーケンスの最初のバイト (10xxxxxx で始まる) は無効ですか?継続バイトが欠落していませんか?バイト出力ボタンは、調査において最も明確なフォークを提供します。 hex が表示されてもテキストのデコードが失敗する場合、Base64 解析は成功し、ペイロードはバイナリであるか、破損しているか、または別の文字セットでエンコードされています。 Base64 句読点を変更しても、正しいバイトがすでに出現した後で文字セットの不一致を修復することはできません。
これでカバーされないもの — UTF-16 ペイロード、画像などのバイナリ出力、および無効なバイトの処理
パターンは一貫しています。単一バイトの 0xFF は UTF-8 では決して有効ではありません。 ASCII バイトにすることはできません (0 ~ 127 のみが ASCII です)。 また、先頭バイトにすることはできません (先頭バイトは 0xC0 ~ 0xFD、0xFF は予約されています)。単独のサロゲート (UTF-16 概念) は UTF-8 には現れません。バイト シーケンス 0xED 0xA0 0x80 (サロゲート U+D800 を UTF-8 スタイルでエンコードする) が表示される場合、それは有効な UTF-8 ではありません。
過去の回避策の 1 つは、btoa(unescape(encodeURIComponent(text))) です。 encodeURIComponent は、café を %C3%A9 (パーセントエンコーディング UTF-8 バイト) に変換し、unescape をコード単位として再パックし、btoa はコード単位をエンコードします。これはほとんどのテキストでは機能しますが、単独のサロゲートの場合は脆弱で、読みにくくなります。最新のパイプライン (TextEncoder からバイト、次に Base64) は、より明確で標準的です。 TextEncoder はすべての最新のブラウザーと Node.js に組み込まれており、正しい選択が可能です。 UTF-16、従来のシングルバイトエンコーディングおよび任意のファイルコンテンツには、それらのバイト用に選択されたデコーダまたはバイナリ対応ビューアが必要です。 ToolAcre は意図的にそれらのことを推測しません。推測すると、無効なシーケンスが誤解を招くテキストになる可能性がありますが、16 進ダンプでは、後で情報に基づいて解釈できるようにすべてのバイトが保存されます。
要点: Base64 ではバイトが得られ、UTF-8 ではテキストが得られます。デコードされたテキストが入力と正確に一致するように、Base64 エンコーダーとデコーダーが両方のステップをどのように実行するか
Base64 文字列があり、UTF-8 テキストが必要な場合、完全な手順は次のとおりです: Base64 をバイトにデコードし (atob または Base64 デコード ライブラリを使用)、バイトから Uint8Array を作成し、文字セット utf-8 を使用して配列を TextDecoder に渡し、結果を文字列として読み取ります。
入力がテキストではなくバイナリ データの場合は、TextDecoder をスキップしてバイトを直接調べます。 Base64 エンコーダーとデコーダーは、厳密な UTF-8 デコードでは拒否される値を保持しながら、16 進数のバイト ビューを提供します。このフォークは診断用です。成功したバイト数と失敗したテキストは、Base64 解析が機能したことを意味しますが、ペイロードはバイナリであるか、破損しているか、このツールが推測できない文字セットでエンコードされています。