UTF-8 を手で組み立てる
コードポイントのビットを 1〜4 バイトのテンプレートに流し込む — UTF-8 の変換を U+3042 → E3 81 82 で手計算し、ASCII 互換の理由と BOM まで確かめる。
「なぜ 3 バイトなのか」を、計算で答えられるようになる
「UTF-8 だと日本語は 3 バイト」— どこかで聞いたことはあっても、なぜ 3 なのか、その 3 バイトの中身が何なのかを説明できるでしょうか。この章の目標は 1 つ、U+3042「あ」が E3 81 82 になるまでを自分の手で導くことです。一度導けば、UTF-8 は暗記物ではなく「計算できるもの」に変わります。
UTF-8 は可変長の符号化です。コードポイントの大きさに応じて、1 文字を 1〜4 バイトで表します。
| コードポイントの範囲 | バイト数 | テンプレート(x に番号のビットを流し込む) |
|---|---|---|
| U+0000〜U+007F | 1 | 0xxxxxxx |
| U+0080〜U+07FF | 2 | 110xxxxx 10xxxxxx |
| U+0800〜U+FFFF | 3 | 1110xxxx 10xxxxxx 10xxxxxx |
| U+10000〜U+10FFFF | 4 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
テンプレートの黒い部分(1110 や 10)は固定の「印」です。先頭バイトの 1 の個数が文字のバイト数を名乗り、続きのバイトは必ず 10 で始まる — この規則だけで、バイト列のどこからでも文字の切れ目が見つけられます。
手計算 — U+3042 を 3 バイトにする
「あ」U+3042 は U+0800〜U+FFFF の範囲なので 3 バイトのテンプレートを使います。手順は 2 ステップです。
0011 0000 0100 0010。右から 6 ビットずつ切ると 0011 / 000001 / 000010(4 + 6 + 6 ビット)11100011 10000001 10000010 → 16 進に直すと E3 81 82検算してみましょう。E3 = 1110 0011 — 先頭に 1 が 3 個なので「3 バイト文字の先頭」。81 = 1000 0001、82 = 1000 0010 — どちらも 10 で始まる「続きのバイト」。印を剥がして残りを繋げば 0011 000001 000010 = 0x3042 に戻ります。行きも帰りも、ただのビットの詰め替えです。
コードポイントのビットがテンプレートに流し込まれる様子。色の付いたビットが「あ」の番号の中身で、黒い部分は UTF-8 の固定の印です。
小問 3-1 — テンプレートに流し込む
本文の手順どおり、紙に 2 進数を書きながら確かめてください。
Q1. UTF-8 のバイト列を先頭から読むとき、そのバイトが「何バイト文字の先頭か」はどう分かりますか。
UTF-8 は先頭バイト自身が長さを名乗る設計です。先頭の 1 の個数がそのまま文字のバイト数(110→2、1110→3、11110→4)で、続くバイトはすべて 10xxxxxx。この規則のおかげで、途中のバイトから読み始めても文字の切れ目をすぐ見つけられます。固定長ではなく、1〜4 バイトの可変長です。
Q2. U+3042(2 進で 0011 0000 0100 0010)を 3 バイトのテンプレート 1110xxxx 10xxxxxx 10xxxxxx に流し込む正しい手順はどれですか。
テンプレートの x は前から 4 個・6 個・6 個です。コードポイントのビット列を右から 6・6 で切ると 0011 / 000001 / 000010 になり、埋めると 11100011 10000001 10000010 = E3 81 82。16 進の 30 42 を「そのまま置く」方式は UTF-16 の発想であって、UTF-8 はビットの再配置を行う点が違います。
Q3. ASCII 文字は UTF-8 で 1 バイト、日本語の漢字やひらがなの多くは 3 バイトになります。文字列「Hello世界」は UTF-8 で何バイトですか。
Hello は ASCII 5 文字 × 1 バイト = 5 バイト、「世」(U+4E16)と「界」(U+754C)はどちらも U+0800〜U+FFFF の範囲なので 3 バイトずつで 6 バイト。合計 11 バイトです。「文字数 = バイト数」が成り立たないことを、具体的な数で体に入れておきましょう。データベースの列幅設計などで必ず効いてきます。
なぜ ASCII 互換が「効く」のか
1 バイトのテンプレート 0xxxxxxx をよく見てください。U+0000〜U+007F、つまり ASCII の 128 文字は、ASCII 時代とまったく同じ 1 バイトになります。英数字だけのファイルは、ASCII として読んでも UTF-8 として読んでも同一 — 既存のプログラムやプロトコルをほとんど変えずに移行できたのは、この設計のおかげです。
もう 1 つ、見逃されがちな急所があります。2 バイト以上の文字を構成するバイトは、先頭(11 で始まる)も続き(10 で始まる)も必ず 0x80 以上です。つまり、日本語の文字のバイト列の途中に、/(0x2F)や "(0x22)のような ASCII の記号と同じ値が絶対に現れません。区切り文字をバイト単位で探すプログラムが、文字の途中を誤って区切る事故が構造的に起きないのです。この安心が当たり前でなかったことを、次章の Shift_JIS で思い知ることになります。
シミュレータで確かめる
下のシミュレータに好きな文字列を入れると、この章で手計算した変換をまとめて確認できます。まずは初期値の「こんにちは」で、5 文字が 15 バイトになること、どの文字も E3 で始まることを見てください。自分の名前、絵文字、英日混在の文字列もおすすめです。入力はブラウザ内だけで処理され、サーバには送信されません。
シミュレータの動作概要(テキスト版): テキスト入力欄に文字列を入れると、文字ごとにコードポイント(U+XXXX)・UTF-8 のバイト列(16 進)・UTF-16 のコード単位が表になって更新されます。あわせて UTF-8 の総バイト数・UTF-16 のコード単位数・コードポイント数・見た目の文字数(書記素)の 4 つの数値が表示され、最後に「この UTF-8 バイト列を Shift_JIS として誤読するとどう見えるか」が表示されます(お使いのブラウザが Shift_JIS のデコードに対応していない場合、その欄には「お使いのブラウザでは表示できません」と表示されます)。
BOM — ファイル先頭の小さな「しるし」
シミュレータの最後の欄で、UTF-8 のバイト列を Shift_JIS として誤読した姿を見ました。「読む側が符号化を当てられるように、ファイルの先頭に目印を置けないか」— その答えの 1 つが BOM(Byte Order Mark)です。コードポイント U+FEFF を UTF-8 で符号化した EF BB BF の 3 バイトをファイルの先頭に置きます。
名前のとおり、本来は UTF-16 でバイトの並び順(上位が先か下位が先か)を示すための仕組みです。UTF-8 にはバイト順の問題がないので、BOM は純粋に「これは UTF-8 です」という任意の目印として使われます。付いていれば手がかりになる一方、先頭の 3 バイトを想定していないプログラムには余計なデータとして誤動作の種にもなります。「BOM は必須ではない。ただし Excel など、BOM を頼りに符号化を判定するソフトがある」— この綱引きの実務的な帰結は第 6 章で扱います。
小問 3-2 — ASCII 互換と BOM
UTF-8 が世界標準になれた理由と、ファイル先頭の「しるし」を確認します。
Q4. UTF-8 の「ASCII 互換」が安全に成り立つ理由として正しいものはどれですか。
0x00〜0x7F は ASCII とビット単位で同一、そして 2 バイト以上の文字を構成するバイト(先頭も続きも)は必ず最上位ビットが 1、つまり 0x80 以上です。だから英数字だけのファイルはそのまま有効な UTF-8 であり、逆に日本語の途中に 0x2F(/)などの ASCII 値が現れることもありません。この設計が、既存の ASCII 前提のプログラムと共存できた最大の理由です。第 4 章で見る Shift_JIS は、まさにここで問題を抱えます。
Q5. 「こんにちは」(ひらがな 5 文字、すべて 3 バイト)は UTF-8 で何バイトですか。
3 バイト × 5 文字 = 15 バイトです。E3 81 93(こ)E3 82 93(ん)E3 81 AB(に)E3 81 A1(ち)E3 81 AF(は)と、どの文字も E3 で始まっていることに注目してください — ひらがなのコードポイント(U+3040 台)が近いので先頭バイトがそろうのです。この「E3 だらけ」が、第 7 章で文字化けの指紋として効いてきます。
Q6. UTF-8 の BOM(Byte Order Mark)の説明として正しいものはどれですか。
BOM の実体は U+FEFF を符号化した EF BB BF の 3 バイトです。UTF-16 ではバイト順(どちらのバイトが先か)を示す実用的な役割がありますが、UTF-8 にバイト順の問題はないため、純粋に「UTF-8 ですよ」という目印としてだけ機能します。必須ではなく、付けると先頭 3 バイトを想定しないプログラムが誤動作することもあります。一方で Excel の CSV 認識には BOM が効く — この実務の綱引きは第 6 章で扱います。
この章で持ち帰ること
- UTF-8 は 1〜4 バイトの可変長。先頭バイトの 1 の個数がバイト数を名乗り、続きは必ず 10 で始まる
- U+3042 → 0011 / 000001 / 000010 → E3 81 82。変換はビットの詰め替えにすぎない
- ASCII は UTF-8 でも同じ 1 バイト、多バイト文字の構成バイトはすべて 0x80 以上 — この 2 点が ASCII 互換の芯
- BOM は U+FEFF の符号化 EF BB BF。UTF-8 では必須でない「目印」
次章は、UTF-8 より前から日本語を支えてきた Shift_JIS へ。「多バイト文字の途中に ASCII の値が現れない」という UTF-8 の安心が、いかに貴重だったかが分かります。