日本語

開発者ツール · Base64 エンコーダおよびデコーダ

btoa() が絵文字をスローする理由と、JavaScript で UTF-8 を Base64 エンコードする方法

· 仕組み

Base64 ユニコード エンコーディング

Unicode 文字は Base64 エンコードの前に UTF-8 bytes に変換されます
オリジナル ToolAcre ベクトル イラスト

btoa() は U+00FF までの文字のみを受け入れるため、アクセント付きテキスト、CJK、絵文字はスローされます。この投稿では、関数が実際に何を期待しているのか、そして TextEncoder がどのようにして正しい UTF-8 Base64 文字列を取得するのかを示します。

btoa が Unicode をスローし、黙ってアクセントを誤ってコード化できる理由

btoa("😀") を呼び出すと、絵文字が 1 バイト サイズのコード単位に収まらないため、InvalidCharacterError がスローされます。微妙なエラーは btoa("é") です。プリコンポーズされた é は U+00E9、256 未満であるため、btoa はそれを受け入れますが、UTF-8 bytes C3 A9 ではなく Latin-1 byte E9 をエンコードします。 e と結合記号で書かれた同じ目に見えるアクセントは、記号が許容範囲外にあるためスローされる可能性があります。ワークブックの略語「アクセントがスローされる」には、この条件が必要です。文字列は大声で失敗することも、静かに間違ったバイトを生成することもあります。

btoa() が実際にエンコードするもの: コード単位 0 ~ 255 のバイナリ文字列 — この関数が Unicode テキストではなく Latin-1 bytes を中心に設計された理由

btoa は「バイナリ文字列」を使用します。各 JavaScript 文字コード単位は 0 ~ 255 の範囲内にある必要があり、1 バイトを表します。 Unicode テキスト エンコーディング、言語、正規化は理解できません。アストラル絵文字は 2 つの UTF-16 サロゲート コード単位で表され、どちらも 255 よりはるかに大きいため、生の JavaScript 文字列を直接送信することはできません。出力を抽象文字ではなくバイトのエンコードとして扱います。

最初に UTF-8、2 番目に Base64 — Base64 アルファベットが適用される前にテキストがバイトになる必要がある理由

TextEncoder は、まず JavaScript 文字列を UTF-8 byte シーケンスに変換します。次に、各バイトをバイナリ文字列文字に変換し、そのバイナリ文字列を btoa に渡すか、バイトを直接受け入れる別の API を使用します。デコードの場合、atob はバイナリ文字列を返します。そのバイト値を回復し、TextDecoder("utf-8") に渡します。 ToolAcre は、置換文字を黙って挿入するのではなく、不正な形式の UTF-8 を拒否する厳密なデコーダを使用します。

動作した例: TextEncoder と btoa を使用した 'café 😀' のエンコード — バイト シーケンス、中間バイナリ文字列、および最終出力

リテラル テキスト カフェ 😀 の場合、UTF-8 bytes は 16 進数で 63 61 66 C3 A9 20 F0 9F 98 80: ASCII c-a-f、é は 2 バイト、スペース、絵文字は 4 バイトです。これら 10 バイトの Base64 は Y2Fmw6kg8J+YgA== です。パディングとアルファベットはバイトのみを表します。彼らは言語にラベルを付けません。直接の btoa("café 😀") の失敗を ToolAcre の UTF-8 モードと比較し、その結果をデコードして、同じ表示アクセントと絵文字が生き残ることを確認します。

古い unescape(encodeURIComponent()) のトリックとそれがハッキングである理由 - 内部で何が行われ、なぜ推奨されないのか

従来の回避策は btoa(unescape(encodeURIComponent(text))) です。 encodeURIComponent は UTF-8 をパーセント エンコードし、unescape はパーセント トリプレットを単一のコード単位として再パックしますが、unescape は非推奨であり、読みにくく、不正な形式の唯一のサロゲートに関して扱いにくいです。 URL が存在しない場合でも、変換は URL 処理のように見えます。 TextEncoder は意図した境界を明確に示します。テキストは一度バイトになり、その後のみ Base64 が動作します。

反対側でのデコード — atob と TextDecoder をペアリングすることでラウンドトリップがロスレスになります

atob の後は、任意のバイナリ バイトに対して decodeURIComponent を呼び出して、それらがテキストになることを期待しないでください。文字コードを Uint8Array に変換し、TextDecoder に渡します。カフェ 😀 の例では、結果は元の 10 バイトの UTF-8 シーケンスであり、その後元の文字列になります。 Base64 が画像または圧縮ファイルのバイトにデコードされる場合、有効な UTF-8 テキストをまったく表現できない可能性があります。 ToolAcre は、バイナリ データを装うのではなく、読みやすい散文であると報告しています。

これでカバーされないもの — ファイルおよびバイナリ BLOB エンコーディング、base64url バリアント、およびストリーミング大規模入力

この説明は、UTF-8 でエンコードされたテキストに関するものです。ファイルと BLOB の Base64、JWT セグメントの Base64url、およびマルチギガバイト データのインクリメンタル エンコーディングでは、インターフェイスやメモリのニーズが異なります。また、Base64 はトークンを暗号化しません。トークンを保持している人は誰でもバイトをデコードできます。デコーダは一般的な欠落パディングやホワイトスペースを受け入れることができますが、相互運用性は依然としてペイロードがテキストであるか任意のバイナリ データであるかを認識することに依存します。

要点: 文字列ではなくバイトをエンコードし、ラウンドトリップをチェックする — Base64 エンコーダーとデコーダーがどのように UTF-8 ステップを実行して、アクセント、CJK、絵文字が生き残るか

生の JavaScript 文字列ではなくバイトをエンコードし、ラウンドトリップを確認します。 Base64 エンコーダーとデコーダーは、ブラウザーに貼り付けられたテキストを保持しながら、TextEncoder と TextDecoder の手順を実行します。 RFC 4648 では、アルファベットとパディングを指定します。 UTF-8 は、文字からバイトへの個別の契約を提供します。これら 2 つのレイヤーを混合すると、InvalidCharacterError と静かな Latin-1 破損の両方の原因になります。