cv.cm

基礎知識 · テキスト・データ・セキュリティ

Unicode と UTF-8 の違いは?

約8分で読めます

結論

Unicode は文字集合で、「é」の U+00E9 のように、すべての文字にコードポイントと呼ばれる番号を割り当てます。UTF-8 は文字コード(エンコード方式)で、その番号をバイト列として保存するための規則です。ASCII は 1 バイト、それ以外は 2〜4 バイトを使うので、「é」は C3 A9 になります。Unicode はどの文字かを決め、UTF-8 はそれをディスクに書いたりネットワークで送ったりするときの形を決めます。

16進数を開く

まずコードポイント、次にバイト

Unicode は U+0000 から U+10FFFF までのコード空間を定めており、1,114,112 個のコードポイントを、65,536 個ずつ 17 の面に分けて収めています。そのうち 150,000 個以上に文字が割り当てられ、現代と歴史上の文字、記号、絵文字をカバーしています。コードポイントは U+ に続けて4桁以上の16進数で書きます。

コードポイントは番号にすぎません。テキストを保存したり送ったりするにはバイト列に変換する必要があり、その方法はいくつもあります。UTF-8、UTF-16、UTF-32 は、どれも同じ Unicode の文字をエンコードする方式です。

文字コードポイントUTF-8 のバイト列UTF-16(ビッグエンディアン)
AU+00414100 41
éU+00E9C3 A900 E9
€U+20ACE2 82 AC20 AC
日U+65E5E6 97 A565 E5
😀U+1F600F0 9F 98 80D8 3D DE 00

UTF-8 が 1・2・3・4 バイトを選ぶ仕組み

RFC 3629 で定められた UTF-8 は可変長のエンコードです。先頭バイトの上位ビットで後に続くバイト数を示し、後続バイトはすべてビット 10 で始まります。そのためストリームは自己同期的で、文字の途中から読み始めたデコーダーも次の先頭バイトまで読み飛ばせます。

コードポイントの範囲バイト数ビットパターン
U+0000 – U+007F10xxxxxxx
U+0080 – U+07FF2110xxxxx 10xxxxxx
U+0800 – U+FFFF31110xxxx 10xxxxxx 10xxxxxx
U+10000 – U+10FFFF411110xxx 10xxxxxx 10xxxxxx 10xxxxxx

計算例:「é」は U+00E9 で、2進数では 000 1110 1001 です。2 バイトのパターンが必要です。上位 5 ビット 00011 を 110xxxxx に入れると 11000011(C3)、下位 6 ビット 101001 を 10xxxxxx に入れると 10101001(A9)になります。

最初の 128 個のコードポイントは ASCII と同じ 1 バイトにエンコードされるため、正しい ASCII ファイルはそのまま正しい UTF-8 でもあります。この後方互換性こそ、UTF-8 が Web の標準になった最大の理由で、現在では Web サイトの約 98% が UTF-8 を使っています。

UTF-8・UTF-16・UTF-32 の比較

エンコード単位のサイズ1文字あたりのバイト数主な使用場面
UTF-88 ビット1–4Web ページ、JSON、Linux・macOS のファイル、ほとんどの API
UTF-1616 ビット2 または 4JavaScript の文字列、Java、Windows API
UTF-3232 ビット常に 4固定長が便利な内部処理

UTF-16 は U+FFFF より大きい文字を、16 ビット単位2つのサロゲートペアとして保存します。JavaScript で "😀".length が 2 になるのはこのためで、この言語は文字ではなく UTF-16 の単位を数えます。[..."😀"].length のように反復すれば 1 が返ります。

英語のテキストでは UTF-8 が最もコンパクトです。中国語・日本語・韓国語が中心のテキストでは、これらの文字が UTF-8 では 3 バイト、UTF-16 では 2 バイトなので、UTF-16 のほうが小さくなることがあります。

バイトオーダーマーク(BOM)

BOM は、ファイルの先頭に置かれる文字 U+FEFF です。UTF-16 と UTF-32 では、バイト列がビッグエンディアンかリトルエンディアンかを読み手に伝えます。UTF-8 では EF BB BF にエンコードされますが、UTF-8 にはバイト順がないため、目印としての役割しかありません。

Unicode の FAQ は、UTF-8 の BOM は必須でも推奨でもないとしています。それでも一部の Windows のエディターは BOM を付け、その結果、シェルスクリプト(#! の行が先頭でなくなる)、CSV のヘッダー、BOM を想定していない JSON パーサーが動かなくなります。ファイルの先頭に  と表示されたら、それは UTF-8 の BOM を Windows-1252 として読んだものです。

文字化けで é や ��� になる理由

文字化け(英語では mojibake)は、書き込んだときとは別の文字コードでバイト列をデコードしたときに起こります。

  • é が é になる:UTF-8 のバイト列 C3 A9 を Windows-1252 や Latin-1 として読んだため、C3 が Ã、A9 が © に対応しています。
  • �(U+FFFD、置換文字):デコーダーが UTF-8 として正しくないバイト列に出会いました。Latin-1 のテキストを UTF-8 として読んだ場合によく起こります。
  • データベースの中の疑問符:列や接続の文字セットがその文字を扱えなかったため、書き込み時に置き換えられ、元の文字は失われています。

対策は、あらゆる境界で文字コードを宣言することです。HTML では <meta charset="utf-8">、HTTP ヘッダーでは Content-Type: text/html; charset=utf-8、データベースの列と接続には UTF-8 を指定します。MySQL では utf8mb4 を使いましょう。古い utf8 文字セットは1文字最大 3 バイトまでしか保存できず、絵文字を受け付けません。

よくある質問

UTF-8 と Unicode は同じものですか?

いいえ。Unicode は文字とその番号の一覧で、UTF-8 はその番号をバイト列として保存する方法の1つです。UTF-8 のテキストのことを「Unicode」と呼ぶ人もよくいます。

UTF-8 では1文字は何バイトですか?

1〜4 バイトです。ASCII の英字は 1 バイト、ヨーロッパのアクセント付き文字の多くは 2 バイト、中国語・日本語・韓国語の文字の多くは 3 バイト、絵文字は 4 バイトです。

UTF-8 はすべての文字を表せますか?

はい。UTF-8 は U+0000 から U+10FFFF までのすべての Unicode コードポイントをエンコードできるので、Unicode が定義するどの文字も UTF-8 で書けます。

ファイルは BOM 付きで保存すべきですか?

UTF-8 なら、ふつうは不要です。任意のものでバイト順の役にも立たず、先頭のバイトが内容であることを前提とするツールを混乱させます。ただし古い Excel では、UTF-8 の CSV ファイルを正しく開くために BOM が必要なことがあります。

基礎知識の記事一覧