Base64 形式を扱う必要がありますか?それならこのサイトが最適です!データをエンコードまたはデコードするために便利なオンラインツールをご利用ください。

Dart での Base64 エンコード:完全ガイド

バイトを持っていて、文字列が必要になるときがあります。ペイロードはファイルかもしれない、認証情報かもしれない、設定トークンかもしれない、JSON ドキュメントの中で運ばれているバイナリの塊かもしれない。そしてチャネルが受け付けるのはテキストだけ。Base64 はそれを解決する取引です:入力の3バイトがすべて、64文字のアルファベットから選んだ4文字になるので、出力は常にきれいな4の倍数で、テキスト専用の世界では常に安全です。代償は固定で、文字数は33%増え、最後のチャンクが短いときは、形式が末尾に = パディング文字を1つか2つ追加します。このガイドは、その取引を正しく行うための Dart レシピです。

インストールするものは何もない。Base64 は2015年の Dart 1.13 から dart:convert に同梱されており、API はそれ以降ずっと安定しています。標準と URL 安全の両方のアルファベットは、10年以上前から利用可能でした。フォーマットの詳しい解説はホームページにあります。ここではエンコーディング側です:API サーフェスの全体像、最もよくあるバグを避けるバイト優先の規律、パディングとアルファベットの選択、そして実世界の仕事たち:JWT、data URI、ファイルアップロード、HTTP ヘッダー、MIME、設定、ストリーム、コマンドライン。デコード、つまり逆方向には専用のガイドがあり、最後にリンクしています。

import 1つ、アルファベット2つ、パディングのルール1つ

エンコーディングの公開インタフェースはすべて dart:convert にあります:

エントリ アルファベット いつ使うか
base64Encode(bytes) 標準:A-Z a-z 0-9 + /、パディングあり API、MIME、Basic 認証、ほとんどの利用側
base64UrlEncode(bytes) URL 安全:A-Z a-z 0-9 - _、パディングあり URL、ファイル名、JWT、オブジェクト ID
base64.encode(bytes) 標準。トップレベル呼び出しと同一 ストリーム変換とコーデックのパイプライン
Base64Encoder().convert(bytes) 標準 名前付きのエンコーダーインスタンスが欲しいとき

2つのルールで4行すべてをカバーできます。第一に、入力はバイト値のリスト、つまり 0 から 255 の整数でなければなりません。それ以外、負の数や 256 以上を含むものはすべて、問題のインデックスを名指しで示す ArgumentError を投げます。第二に、出力は常にパディングされます:パディングなしの出力を作るフラグもコンストラクターもオプションもありません。形式のパディングはデータ側の性質だからで、それを無くしたい仕様は、それを別々の、文書化されたステップとして剥がします。最小の例を、端から端まで:

import 'dart:convert';
void main() {
  final text = 'Dart is open source';
  final bytes = utf8.encode(text);
  final encoded = base64Encode(bytes);
  print(encoded); // RGFydCBpcyBvcGVuIHNvdXJjZQ==
}

バイト優先:あなたを救う順序

Dart で最もよくある base64 のバグは、そもそも base64 とは無関係です。操作の順序の話です。Dart の String は UTF-16 のコードユニットの列で、base64Encode(text.codeUnits) を呼び出すと、受信者が期待するバイトではなく、その 16 ビットのユニットが詰まされてしまいます。純粋な ASCII では両者がたまたま一致するので、最初のアクセント付き文字、絵文字、CJK テキストがやってくるまで、バグは姿を隠し続けます。そこでエンコーダーは仕事を拒否します。 0x4e16 のようなコードユニットはバイト値ではないからです:

import 'dart:convert';
void main() {
  final message = 'Héllo Wörld 世界';
  print(utf8.encode(message).length); // 20
  print(message.codeUnits.length); // 14
  print(base64Encode(utf8.encode(message)));
  try {
    base64Encode(message.codeUnits);
  } on ArgumentError catch (e) {
    print(e);
  }
}

ArgumentError はまさに問題のインデックスを指し示すので、失敗は無言ではなく、はっきりと声に出して教えてくれます。守るべき規律:base64 に話す前に、バイトが何であるかを決定してください。テキストは名前付きのエンコーディングを通ります。モダンなデータには utf8.encode で、できた List<int> が詰まされるものです。ファイルやネットワークソケットからのバイトは、すでに Uint8List として到着しているので、変換なしでエンコーダーにちょうどよい形です。

パディング:エンコーダーの仕事

Base64 は3バイトのグループを4文字に写像するので、長さが3の倍数ではないペイロードは、末尾に不完全なグループが残ります。形式はその不足を = 文字で示します:入力バイト1つは4文字 + パッド2つ、2バイトは4文字 + パッド1つ、3バイトはちょうど4文字。Dart のエンコーダーは、これを無条件に、あなたの代わりにやります:

import 'dart:convert';
void main() {
  print(base64Encode([0x41])); // QQ==
  print(base64Encode([0x41, 0x42])); // QUI=
  print(base64Encode([0x41, 0x42, 0x43])); // QUJD
}

その無条件な挙動は機能です:出力は常に、合法で自己記述的な base64 文字列になります。仕様が無パディング版を要求するとき、その典型が JWT ですが、剥がすのはライブラリの設定ではなく、あなたの明示的で見えるステップです:

base64UrlEncode(bytes).replaceAll('=', '')

剥がすのは仕様の境界で、名前をつけて、文書化してください。この取引のデコーダー側、破損した入力や剥がれた入力の修復のしかたを含む話は、デコーディングガイドで扱っています。

URL 安全な Base64

標準アルファベットには +、/、= が含まれ、この3文字は URL 構文と衝突します:クエリの区切り、パスの区切り、パラメータの区切り。RFC 4648 で base64url として標準化された URL 安全アルファベットは、+ を - に、/ を _ に置き換えるので、出力はエスケープなしでパスセグメント、クエリ値、ファイル名のいずれにも置けます。両方の置換文字を試しさせるバイトでの差はこうです:

import 'dart:convert';
void main() {
  final tricky = [0xfb, 0xff, 0xfe, 0xf9];
  print(base64Encode(tricky)); // +//++Q==
  print(base64UrlEncode(tricky)); // -__--Q==
}

選ぶ基準は、利用側で、好みではありません。値が URL、JWT、ファイル名のどこかに住むなら、base64UrlEncode でエンコードし、仕様がパディングなしならパディングを剥がします。値が MIME のボディ、Basic 認証ヘッダー、あるいは「base64」と書かれた API 契約のフィールドなら、標準アルファベットを使います。修飾のついていない base64 は標準の方を意味するからです。2つのアルファベットは、厳しい利用側の目には交換できません:標準 base64 を期待するサーバーは、- を含むペイロードを 400 で、それ以上の助けもなしに拒否するかもしれません。

エンコーディング:あなたが詰めるのはどのバイトか

入力がテキストである場合、エンコーディングのステップが Base64 が見るバイトを決定し、利用側は向こう側でエンコーディングを仮定しています。あなたの仮定と利用側の仮定が異なると、出力は誤ったバイトの、完璧に正当な base64 になってしまいます。これは最悪の種類のバグで、何も例外が投げられないからです。モダンなやり取りでは UTF-8 がデフォルトです。他の1バイトエンコーディングはレガシーデータのために存在します:

エンコーディング 使うもの エンコードに使うもの
utf8 モダンなテキスト、JSON、Web 上のあらゆるもの utf8.encode(text)
latin1 レガシーな西系の1バイトデータ latin1.encode(text)
ascii 素の7ビットテキスト ascii.encode(text)
import 'dart:convert';
void main() {
  final modern = base64Encode(utf8.encode('Héllo'));
  final legacy = base64Encode(latin1.encode('Héllo'));
  print(modern); // SMOpbGxv
  print(legacy); // SOlsbG8=
}

同じ単語、バイトは違う、base64 も違う。長さに注目してください:UTF-8 は Héllo に6バイト必要です。アクセントは2バイトのシーケンスだからで、Latin-1 は5バイトで収めます。利用側があなたが使わなかったエンコーディングでデコードすると、文字化けになります。それはデータが伝送中に壊れたように見えますが、実際には意図の中で壊れたものです。

JWT:トークンを書く

JSON Web Token は、ドットで結ばれた3つの base64url 部分から成ります:ヘッダー、ペイロード、署名。RFC 7515 が2つの詳細を固定しています:アルファベットは URL 安全、パディングは省略。トークンは URL やヘッダーの中で暮らすよう設計されているからです。HS256 アルゴリズムの署名は、header.payload の HMAC-SHA256 で、それ自身はパディングなしの base64url です。crypto パッケージで手作業で作るのは数行で、見た目よりも透過的です:

import 'dart:convert';
import 'package:crypto/crypto.dart';
String base64UrlNoPadding(List<int> bytes) {
  return base64UrlEncode(bytes).replaceAll('=', '');
}
String createJwt(Map<String, dynamic> header, Map<String, dynamic> payload,
    List<int> secretKey) {
  final signingInput =
      '${base64UrlNoPadding(utf8.encode(jsonEncode(header)))}.'
      '${base64UrlNoPadding(utf8.encode(jsonEncode(payload)))}';
  final mac = Hmac(sha256, secretKey).convert(utf8.encode(signingInput));
  final signature = base64UrlNoPadding(mac.bytes);
  return '$signingInput.$signature';
}
void main() {
  final token = createJwt(
    {'alg': 'HS256', 'typ': 'JWT'},
    {'sub': 'user-42', 'exp': 1893456000},
    utf8.encode('a-32-byte-secret-key-0123456789'),
  );
  print(token);
}

署名は、確かに詰まれた正確なバイトの上で計算されるので、あなたが出力するのと同じ文字列に署名していれば、向こう側の検証は同じ手順の繰り返しになります。警告が3つ。pub.dev の古い jwt パッケージは2014年のもので、null safety の前です。エコシステムの実用的な答えは、crypto を使ってここに見せることをすることです。alg: none のトークンを絶対に出力しないでください。クライアントにアルゴリズムを選ばせるのも絶対にやめてください。そして、ペイロードは誰でも読めるので、トークンが証明するつもりのもののみを含めましょう。

Data URI:テキストの中にファイルを送り込む

RFC 2397 で定義される data URI は、ペイロードそのものがデータである URL です。data URI の中のバイナリコンテンツは base64 エンコードされます。だからこの形式は、テキストドキュメントが画像、フォント、添付ファイルを埋め込みたいあらゆる場所に現れます:HTML 属性、CSS、JSON、設定ファイル。Dart は URI をネイティブで構築でき、URI パッケージは不要です:

import 'dart:convert';
import 'dart:io';
Future<void> main() async {
  final png = await File('icon.png').readAsBytes();
  final imageUri = Uri.dataFromBytes(png, mimeType: 'image/png');
  print(imageUri); // data:image/png;base64,iVBOR...
  final note = Uri.dataFromString('Hello, Dart!');
  print(note); // data:,Hello,%20Dart!
}

Uri.dataFromBytes はデフォルトで base64 エンコードします(別の形式には percentEncoded: true というオプトインがあります)。これがバイナリに対して正しいエンコーディングです。Uri.dataFromString はデフォルトでパーセントエンコードします。短いテキストはそれで短いからです。バイト詰めを望むなら base64: true フラグも受け付けます。実用上の落とし穴はスケールです:ペイロードは33%のオーバーヘッドでドキュメントの内部に乗るので、data URI はアイコンやサムネイルのような小さなアセットのためにあります。CSS でメガバイトを運ぶためではありません。

ファイル:テキストチャネルのためにバイトを詰める

日常の仕事:JSON、設定ファイル、あるいはテキスト専用トランスポートを必ず通らなければならないファイル。パターンは、バイトを読み、エンコードし、埋め込むことです:

import 'dart:convert';
import 'dart:io';
Future<void> main() async {
  final image = await File('photo.jpg').readAsBytes();
  final encoded = base64Encode(image);
  final upload = jsonEncode({
    'name': 'photo.jpg',
    'size': image.length,
    'data': encoded,
  });
  print('payload ${upload.length} chars for ${image.length} bytes');
}

頭に入れておくべき数字は増加です:2,000 バイトのファイルは 2,668 文字の base64 になり、JSON キーが加わるともう少し増えます。落とし穴が2つ。第一に、入力がすでにエンコードされていないことを確認してください:すでに base64 の文字列を base64 エンコードするのは、典型的な二重エンコードバグで、それは「うまく」デコードされて、また別の base64 の壁になります。第二に、チャネルがバイナリを運べるなら(multipart/form-data が存在する理由がまさにそれです)、バイナリを運びましょう:4分の1小さく済むし、base64 の税金は純粋な無駄です。

HTTP と API:ヘッダーとペイロード

HTTP で最も馴染みのあるエンコーディングの仕事は、Authorization: Basic ヘッダーです:言葉 Basic、空白、そして username:password の標準アルファベットによる base64:

import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
  final credentials = base64Encode(utf8.encode('octocat:secret'));
  final client = http.Client();
  final response = await client.get(
    Uri.parse('https://httpbin.org/basic-auth/octocat/secret'),
    headers: {'Authorization': 'Basic $credentials'},
  );
  print(response.statusCode);
  client.close();
}

http パッケージなら、dart pub add http 1発の距離で、ヘッダーはリクエストの中のただの文字列です。落とし穴はセキュリティの枠組みにあります:ここでの base64 は保護ではなく曖昧化です。誰でも1ステップで元に戻せるので、Basic 認証が TLS 接続の上でのみ存在してよいのは、そこで保護を行っているのはエンコーディングではなくトランスポートだからです。API のペイロードフィールドについては、契約に従ってください:base64 と書かれていれば、それはパディング付きの標準アルファベットで、URL 安全の変体は厳しい利用側が拒否する別のものです。

メールと MIME: 76 でラップする

メールがバイナリを運べるようにするシステムである MIME は、コンテンツ転送エンコーディングとして base64 を使い、RFC 2045 はエンコードされた行は 76 文字を超えてはならず、行のあいだには CRLF を入れると規定しています。この制限は MIME の慣習です - 76 文字に CRLF を加えても、80 列のディスプレイに余裕で収まるからです - そして準拠しているエンコーダーはすべてラップします。Dart のエンコーダーは途切れない 1 つの文字列を生成するので、ラップは短い後処理のステップです:

import 'dart:convert';
String wrapForMime(String base64Text, [int lineLength = 76]) {
  final buffer = StringBuffer();
  for (var i = 0; i < base64Text.length; i += lineLength) {
    final end = i + lineLength > base64Text.length
        ? base64Text.length
        : i + lineLength;
    buffer
      ..write(base64Text.substring(i, end))
      ..write('\r\n');
  }
  return buffer.toString();
}
void main() {
  final encoded = base64Encode(utf8.encode('Hello from an email attachment'));
  print(wrapForMime(encoded));
}

できた文字列、パディングも含めてラップし、最後の行は 76 文字まで、それがどの長さでもそのままにしておきます。やってはいけない唯一のこと:1 文字節約できるとの期待で、ラップする前にパディングを剥がすことです。パッドはエンコードされたコンテンツの一部であり、行を組み立て直す利用側は、それが無い結果を拒否します。

設定:シークレットを1行にする

引用符や改行、その他の厄介な文字を含むトークン、キー、認証情報は、設定行や CI 変数にきれいに収まるように、ときどき base64 エンコードされます。まず正直な枠組みから:これは暗号化ではなく曖昧化で、リポジトリやログに一度でも到達したものは公開されているとみなすべきです。このパターンは整理のために使い、秘密のためには絶対に使わないでください。値をエンコードするのは1コールです:

import 'dart:convert';
String forEnvFile(String secret) {
  return base64Encode(utf8.encode(secret));
}
void main() {
  final line = 'API_TOKEN_B64=${forEnvFile('sk-live-abc123')}';
  print(line); // API_TOKEN_B64=c2stbGl2ZS1hYmMxMjM=
}

値はその後、.env ファイル、CI シークレット、またはコンパイル時の define に座し、デコード1回で素のテキストとして戻ってきます。シークレットを転送中や保存時に保護する必要があるなら、シークレットマネージャや暗号化ライブラリに手を伸ばしてください。base64 のここでの仕事は、パイプラインのテキスト処理をシンプルに保つだけで、それ以上でもそれ以下でもありません。

ストリーム:チャンク境界をまたいでエンコードする

バイトがチャンクで到着するとき、ネットワークからの読み取り、ブロックごとに処理するファイル、エンコーダーはあなたが何かに揃えることなく対処できます。コーデックは不完全なグループをチャンク境界をまたいで保持するので、チャンクサイズは3の倍数である必要はありません:

import 'dart:convert';
import 'dart:typed_data';
Future<void> main() async {
  final data = Uint8List(100000);
  for (var i = 0; i < data.length; i += 31) {
    data[i] = i % 256;
  }
  final chunks = <List<int>>[data.sublist(0, 777), data.sublist(777)];
  final encoded = await Stream.fromIterable(chunks)
      .transform(base64.encoder)
      .join();
  print('in: ${data.length}, out: ${encoded.length}'); // in: 100000, out: 133336
}

777 バイトと 99,223 バイトという、サイズがぎこちない2つのチャンクが、正しく 133,336 文字の文字列1つを生成します。エンコーダーは、次のチャンクが到着するまで各不完全なグループの残りビットを停めておき、パディングを最後にのみ出力するからです。sink を好むなら、base64.encoder.startChunkedConversion が同じ状態機械を ByteConversionSink としてくれます(あなたはバイトチャンクを与え、それは文字列を出力します)。これは、1つの大きな文字列を一度も結合せずに大きな出力をファイルやソケットに書くのに、自然に合う形です。

ビッグデータ:スループットとメモリ

サイズの計算は正確で、覚えておく価値があります:出力長は、入力長を3で割ったものを切り上げて4倍したものです。1バイト、2バイト、3バイトはどれも4文字のコストで、そこから先は一律に33%のオーバーヘッドです。バッファを予約したり進捗を報告したりするときのための式:

import 'dart:convert';
int encodedLength(int n) => (n + 2) ~/ 3 * 4;
void main() {
  print(encodedLength(100000)); // 133336
}

速度は制約ではありません:エンコーダーはテーブル参照を1パスで行い、メガバイトをミリ秒で処理します。制約は、サイズ税そのものです(ワイヤー上でもメモリでも課税されます)。そして、エンコードされた形が文字列であることです。スケールでは両方を意識してください:大きく成長しうるペイロードは、1つの大きなリストと1つの大きな文字列を溜める代わりに、上記のようにストリームでエンコードし、同じデータの反復転送には、チャネルにバイナリモードがあるかを問いましょう。33%は、どのアルゴリズムでも返金できない恒久的な追加料金だからです。

コマンドラインのエンコーダー

VM はエンコーダーからきれいな CLI を作り出します。このツールはファイル引数または標準入力を読み、標準アルファベットのエンコーディングを表示します:

import 'dart:convert';
import 'dart:io';
Future<void> main(List<String> args) async {
  final bytes = await _read(args);
  stdout.writeln(base64Encode(bytes));
}
Future<List<int>> _read(List<String> args) async {
  if (args.isNotEmpty) {
    return File(args[0]).readAsBytes();
  }
  final all = <int>[];
  await for (final chunk in stdin) {
    all.addAll(chunk);
  }
  return all;
}

それを bin/encode.dart として保存し、dart run bin/encode.dart photo.jpg > photo.b64 を実行します。パイプでも構いません:cat config | dart run bin/encode.dart。相棒である、読んで平坦化するデコーダーはデコーディングガイドの最初の例で、2つのスクリプトを合わせたものは、バイナリをテキストチャネルを通じて動かすための、小さくて本当に役立つツールセットです。

外へ出す道で噛みつく落とし穴

  • codeUnits の罠。base64Encode(text.codeUnits) はバイトではなく UTF-16 のユニットを詰めます。ASCII では動きますが、255 を超える最初のコードユニットで ArgumentError を投げます。テキストは常に、まず名前付きのエンコーディングでエンコードしてください。
  • アルファベットの不一致。標準アルファベットを期待する利用側に URL 安全の出力を渡すのは、起こるのを待っている 400 です。アルファベットは仕様から決め、一度エンコードし、後から変換しない。
  • パディングの仮定。Dart は常にパディングします。仕様がパディングなしを望むなら、境界で明示的なステップとして replaceAll('=', '') で剥がし、契約にもそれを明記してください。
  • エンコーディングのズレ。UTF-8 でデコードする利用者のために Latin-1 バイトをエンコードすると、誤ったデータの正当な base64 になります。何も例外は投げません。テキストはただ間違っているだけです。
  • 二重エンコーディング。すでに base64 である値、別の設定からコピーしてきたトークンなどを base64 エンコードするのは、「デコードするとまた別の base64 の壁になる」典型的なバグです。
  • プライバシーの錯覚。Base64 は暗号方式ではなく形式です。脅威モデルに読者がいれば、答えはエンコーディングではなく暗号化です。
  • 時代遅れのパッケージ。pub.dev の昔からある jwt パッケージは null safety の前からのもので、JWT の仕事には、crypto と上記の数行が保守されている道です。

他の手段に手を伸ばすべきとき

  • HTTP でのファイルアップロード。multipart/form-data を使ってください。生のバイトを運ぶので、33% の税金を完全にスキップできます。
  • 大きなペイロード、または反復するペイロード。先に圧縮し、次にエンコードしてください:gzip したテキストの base64 は、テキストの base64 と比べて劇的に小さく、解凍側はすでに形式を知っています。
  • URL 内の短いテキスト。わずか数文字ならパーセントエンコーディングの方が短く、値が人間が読めるままになります。data URI は、デフォルトでそれを代わりにやってくれます。
  • デバッグ出力とログ。16進は base64 より 50% 長く(生のサイズの2倍、base64 は3分の4倍)ですが、走査しやすく、diff しやすく、同僚に手渡ししやすい。ログの中のバイナリの断片には、通常こちらが勝ちます。

ベストプラクティス、エンコーダーのリスト

  • エンコードするのはバイトであり、決してコードユニットではない。テキストはまず名前付きのエンコーディングを通す。
  • 呼び出しを書く前に、利用側の仕様からアルファベットを決める。
  • パディングを剥がすのは、仕様がパディングなしと言っている場所だけで、境界での見えるステップとして。
  • エンコーディングは契約に明示する。向こう側について何の仮定も持たない。
  • 大きく成長しうるものは、すべてストリームに。
  • base64 はテキスト専用チャネルのための形式として扱い、機密データの保護としては絶対に扱わない。

二つのアルファベットの簡潔な歴史

あなたが先ほど使ったこの形式は、すべての Dart リリースよりも古く、あなたが使えるアルファベットの選択肢は、Dart が到着する何十年前に標準化されていました。短いバージョン:

  • 1993年、RFC 1521:MIME がメールのコンテンツ転送エンコーディングとして base64 を紹介。標準の64文字アルファベットと、この記事がラップする76文字の行制限つきで。バイナリをテキストチャネルを通じて運ぶという形式の仕事は、ここから始まった。
  • 1996年、RFC 2045:base64 のパディングと行長のルールを、持続的な標準とした MIME の廃止置き換え。
  • 2006年、RFC 4648:エンコーディングが MIME から引き剥がされ、単独で標準化され、URL 安全アルファベットと、デコーダーは不正な入力を拒否すべきだという助言が加わった。Dart で得られる2つのアルファベットの選択肢は、この文書から来ている。
  • 2015年、RFC 7515:JSON Web Signatures がパディングなしの base64url を規定。すべての JWT の背後にある慣習。
  • 2015年11月、Dart 1.13:base64 が dart:convert に到着。URL 安全の変体は翌春の Dart 1.16 に続き、上で使ったトップレベルの base64Encode と base64UrlEncode 呼び出しは2018年の Dart 2.0 に着地。
  • 今日、Dart 3.13:両方のアルファベット、常にパディング付き、import 1つ離れ、2015年から不変の、同じ厳しくてシンプルな機械。

33% のオーバーヘッドも、1993年以来変わっていません。これは計算の性質、3バイトに対して4つの記号で、あなたが使うことになるすべての実装、すべての言語で、同じ額を払います。

エンコーディングの台からのトリビア

  • エンコーダーは止められない:SDK にはパディングなし出力のフラグは存在しないため、「パッドを剥がす」のは、常にあなたのコードで、あなたの境界で、誰の目にも見える形で、ということになります。
  • 1バイトが4文字になる:base64Encode([65]) は QQ==。考えうる最も短い base64 文字列は4文字で、そのうち情報を運ぶのは最初の2文字だけ。最後の2文字はパディング。
  • Dart の両方のエンコーダーはパディングする。base64UrlEncode でも同じだ。base64url の「パディングなし」はアルファベットの性質ではなく、RFC 7515 由来の利用側の慣習。
  • 標準アルファベットは7ビット印刷可能になるように設計され、それ以来デフォルトのまま。+ と / がやがて URL 安全な代替文字を得たという事実は、それがいかに中心的になったかを示すものであって、欠陥ではない。
  • Héllo Wörld 世界 の同じ20バイトの UTF-8 は SMOpbGxvIFfDtnJsZCDkuJbnlYw= に詰まる一方、この文字列の14個のコードユニットはインデックス12でエンコーダーをクラッシュさせる。同じ文字、まったく異なる2つの出力、そのうち1つはエラー。
  • PEM ファイル、すべての TLS 証明書にある -----BEGIN CERTIFICATE----- ブロックは、ヘッダー付きで64文字ごとにラップされた base64 で、この形式は1987年に遡り、MIME がメール用に base64 を刊行する6年前のことだ。

これでエンコーディング側の全体を手にしました:API サーフェス、バイト優先の規律、パディングとアルファベットの決断、そして JWT、data URI、ファイル、HTTP、MIME、設定、ストリーム、シェルでの実用的なパターン。逆方向、つまりこれらの文字列のいずれかをばらす仕事で、デコーダーの厳格さ、パーセントエスケープの驚き、修復ツールもすべて含めた話は、Base64 デコーディングガイドで扱われており、すぐ下にリンクしています。

最終更新: 2026-10-10

関連記事: Dart での Base64 デコード:完全ガイド