UTF-8
この記事には複数の問題があります。改善やノートページでの議論にご協力ください。
|
Unicode |
---|
文字符号化スキーム |
UTF-7 |
UTF-8 |
CESU-8 |
UTF-16 |
UTF-32 |
UTF-EBCDIC |
SCSU |
Punycode (IDN/IDNA) |
GB 18030 |
その他 |
UCS |
マッピング |
書字方向 |
BOM |
漢字統合 |
UnicodeとHTML |
Unicodeと電子メール |
Unicodeフォント |
UTF-8(ユーティーエフはち、ユーティーエフエイト)はISO/IEC 10646 (UCS) とUnicodeで使える8ビット符号単位(1–4バイトの可変長)の文字符号化形式および文字符号化スキーム。
正式名称は、ISO/IEC 10646では “UCS Transformation Format 8”、Unicodeでは “Unicode Transformation Format-8” という。両者はISO/IEC 10646とUnicodeのコード重複範囲で互換性がある。RFCにも仕様がある[1]。
2バイト目以降に「/」などのASCII文字が現れないように工夫されていることから、UTF-FSS (File System Safe) ともいわれる。旧名称はUTF-2。
UTF-8は、データ交換方式・ファイル形式として一般的に使われる傾向にある。
当初は、ベル研究所においてPlan 9で用いるエンコードとして、ロブ・パイクによる設計指針のもと、ケン・トンプソンによって考案された[2][3]。
エンコード体系
ASCII文字と互換性を持たせるために、ASCIIと同じ部分は1バイト、その他の部分を2–6バイトで符号化する。4バイトのシーケンスでは21ビット (0x1FFFFF) まで表現することができるが、Unicodeの範囲外となる17面以降を表すもの(U+10FFFFより大きなもの)は受け付けない。
また、5–6バイトの表現は、ISO/IEC 10646による定義[4]とIETFによるかつての定義[5]で、Unicodeの範囲外を符号化するためにのみ使用するが、Unicodeによる定義[6]とIETFによる最新の定義[7]では、5–6バイトの表現は不正なシーケンスである。
後述のセキュリティの項に詳細はあるが、符号化は最少のバイト数で表現しなければならない。そのため、バイト数ごとにUnicodeの符号位置の最小値(下限)も設けている。
例えば、1バイトで表現するASCII文字は2バイト以上でも表現できるが、バイト数ごとの下限によってこれを回避している。
ビットパターンは以下のようになっている。
バイト数 | 有効ビット | Unicode | 2進数表記 | 16進数表記 | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|
1 | 7 bit | 0xxx-xxxx | 00..7F | ||||||||
上限 | U+007F | 0111-1111 | 7F | ||||||||
下限 | U+0000 | 0000-0000 | 00 | ||||||||
2 | 11 bit | 110y-yyyx | 10xx-xxxx | C2..DF | 80..BF | ||||||
上限 | U+07FF | 1101-1111 | 1011-1111 | DF | BF | ||||||
下限 | U+0080 | 1100-0010 | 1000-0000 | C2 | 80 | ||||||
3 | 16 bit | 1110-yyyy | 10yx-xxxx | 10xx-xxxx | E0..EF | 80..BF | 80..BF | ||||
上限 | U+FFFF | 1110-1111 | 1011-1111 | 1011-1111 | EF | BF* | BF | ||||
下限 | U+0800 | 1110-0000 | 1010-0000 | 1000-0000 | E0 | 80* | 80 | ||||
4 | 21 bit | 1111-0yyy | 10yy-xxxx | 10xx-xxxx | 10xx-xxxx | F0..F4 | 80..BF | 80..BF | 80..BF | ||
上限 | U+10FFFF | 1111-0100 | 1000-1111 | 1011-1111 | 1011-1111 | F4 | BF* | BF | BF | ||
下限 | U+10000 | 1111-0000 | 1001-0000 | 1000-0000 | 1000-0000 | F0 | 80* | 80 | 80 |
* 第1バイトがE0のときに第2バイトが80-9Fの範囲を、または同F0のときに80-8Fの範囲を取るものは冗長な符号化となるため許されない。第1バイトがEDのときに第2バイトがA0以上となるものはサロゲートペアのための符号位置にあたり、また同F4のときに90以上となるものはUnicodeの範囲外となるため、UTF-8ではやはり許されない。
Unicodeの符号位置を2進表記したものを、上のビットパターンのx, yに右詰めに格納する(最少のバイト数で表現するため、yの部分には最低1回は1が出現する)。符号化されたバイト列は、バイト順に関わらず左から順に出力する。
1バイト目の先頭の連続するビット "1"(その後にビット "0" が1つ付く)の個数で、その文字のバイト数がわかるようになっている。また、2バイト目以降はビットパターン "10" で始まり、1バイト目と2バイト目以降では値の範囲が重ならないので、文字境界を確実に判定できる。すなわち、任意のバイトの先頭ビットが "0" の場合は1バイト文字、"10" の場合は2バイト以上の文字の2番目以降のバイト、"110" の場合は2バイト文字の先頭バイト、"1110" の場合は3バイト文字の先頭バイト、"11110" の場合は4バイト文字の先頭バイトであると判定できる。
7バイト以上の文字は規定されないため、0xFE
, 0xFF
は使用されない。このため、バイト順マーク (BOM) に0xFE
と0xFF
を使用するUTF-16やUTF-32が、UTF-8と混同されることはない。
特徴
利点
- ASCII文字コードのテキストを処理するソフトウェアの多くがそのまま使える[8]。
- バイトストリーム中の任意の位置から、その文字、前の文字、あるいは次の文字の先頭バイトを容易に判定することができる。
- 文字列の検索を単なるバイト列の検索として行っても、文字境界と異なる個所でマッチしてしまうことがない。たとえばShift_JISで「¥」(0x5C) を検索すると「表」(0x95 0x5C) の2バイト目にマッチしたり、EUC-JPで「海」(0xB3 0xA4) を検索すると「ここ」(0xA4 0xB3 0xA4 0xB3) にマッチしたりするのと同様のことが起きない。このため、マルチバイト文字を意識せず、ISO 8859-1などの8ビット文字向けに作られた膨大なプログラム資産を、比較的少ない修正で再利用できる。
- ただし、他のUnicodeの符号化と同様に、単にバイト列の比較では文字列が同一か判断できない場合がある。詳細は、Unicodeの等価性および正規化を参照のこと。
- UTF-16やUTF-32と異なり、バイト単位の入出力を行うため、バイト順の影響がない。
- 21ビットまで表現できるため、サロゲートペアを使用する必要がない。
- ASCII文字が主体の文書であれば、ほとんどデータサイズを増やさずにUnicodeのメリットを享受できる。UTF-16やUTF-32では、データサイズはほぼ2倍、4倍となる。
- 複数のUTF-8文字列を、単なる符号なし8ビット整数の配列とみなして辞書順ソートした結果は、Unicodeの符号位置の辞書順のソート結果(すなわちUTF-32に変換した後にソートした結果)と等しくなる。これに対して、サロゲートペアを含むUTF-16文字列を符号なし16ビット整数の配列とみなしてソートした結果は、Unicodeの符号位置の辞書順のソート結果と異なりうる。
欠点
- UTF-8による符号化では、漢字や仮名などの表現に3バイトを要する。このように、東アジアの従来文字コードではマルチバイト符号を用いて1文字2バイトで表現されていたデータが、1.5倍かそれ以上のサイズとなる。同様に、ISO/IEC 8859-1では1バイトで表現できた非ASCIIのラテン文字(ウムラウト付きの文字など)も2バイトとなるし、その他のISO/IEC 8859シリーズに属する文字符号ではデータ量がさらに増大しうる。
- 最短ではない符号やサロゲートペアなど、UTF-8の規格外だがチェックを行わないプログラムでは一見正常に扱われるバイト列が存在する。これらのバイト列を入力として受け入れてしまうと、プログラムが予期しない範囲のデータを生成するため、セキュリティ上の脅威となりうる[9]。
サロゲートペアの扱い
UTF-16ではサロゲートペアで表されるような、基本多言語面外の符号位置をUTF-8で表す時は、変換元がUTF-16でサロゲートペアの時には U+D800
–U+DBFF
, U+DC00
–U+DFFF
を表すUTF-8にそのまま変換したりはせず、U+10000
–U+10FFFF
の符号位置にデコードしてから変換する。そのままUTF-8で符号化したような列は不正なUTF-8とされる。
サロゲートペアのままUTF-8と同等の符号化を行う符号化は、CESU-8 (Compatibility Encoding Scheme for UTF-16: 8-Bit) として別途定義されている。実用に供されている例としては、Oracle Databaseのバージョン8以前において、UTF-8として3オクテットまでのオクテット列しか扱えなかったために定義されたものである。本来のUTF-8における4オクテット列の代わりに、サロゲート符号位置を表す3オクテット列のペア(上位が ED A0 80
–ED AF BF
、下位が ED B0 80
–ED BF BF
)で表現される。
現在のOracle Databaseでも、CESU-8を「UTF8」として、「普通のUTF-8」を「AL32UTF8」として扱っているため注意を要する。MySQLでも「utf8」を指定した場合は4オクテット列が扱えず、CESU-8相当の符号化を必要とする(4オクテット列対応のUTF-8は「utf8mb4」として別途定義されているが、MySQL 5.5.3以降でないと使用できない[10])。
また、Javaの一部の内部実装で用いられているModified UTF-8も、サロゲートペアをそのまま残す仕様となっている。ただし、NULL文字をC0 80
とエンコードする(これもUTF-8規格外)点で、CESU-8とも異なる実装となっている。
セキュリティ
UTF-8のエンコード体系には冗長性があり、同じ文字を符号化するのに複数の表現が考えられる(例: スラッシュ記号である「/」を 0x2F という1バイトで表現するのではなく、0xC0 0xAF という2バイトもしくはそれより大きなバイト数で表現する)。かつてはそのような表現も許容されていたが、ディレクトリトラバーサルなどの対策として行われる文字列検査を冗長な表現によりすり抜ける手法が知られるようになったため、現在の仕様では最少のバイト数による表現以外は不正なUTF-8シーケンスとみなさなければならない[11]。
ISO/IEC 10646の定義が5バイト以上の表現を許容していることにより、正しくない実装を行ったバグのあるシステムにおいてエンコード時にバッファオーバーフローが発生する可能性も指摘されている。
文字種
B | Unicode | スクリプト | JIS X 0201 | JIS X 0208 | JIS X 0212 | JIS X 0213 |
---|---|---|---|---|---|---|
1 | U+0000–U+007F | ASCII | Roman(円記号・オーバーライン以外) | |||
2 | U+0080–U+07FF | 円記号 | 非漢字の一部 | 非漢字の一部 | 非漢字の一部 | |
3 | U+0800–U+FFFF | オーバーライン、Kana | 残りの全て | 残りの全て | 大半 | |
4 | U+10000–U+10FFFF | 古代文字、3に含まれない漢字 | 第3・第4水準漢字の一部 |
バイト順マークの使用
UTF-8で符号されたテキストデータはエンディアンに関わらず同じ内容になるので、バイト順マーク (BOM) は必要ない。しかし、テキストデータがUTF-8で符号化されていることの標識として、データの先頭にEF BB BF(16進。UCSでのバイト順マークU+FEFFのUTF-8での表現)を付加することが許される。一部のテキスト処理アプリケーション(テキストエディタなど)がBOMを前提とした動作をすることがある。TeraPad、EmEditor、MIFESのようにBOMを付加するかどうかを選択できるものもある。
なお、日本の特殊事情として、このシーケンスがある方をUTF-8、ない方を特にUTF-8Nと呼ぶこともある[12]が、このような呼び分けは日本以外ではほとんど知られておらず、また公的規格などによる裏付けもない[13]。
このシーケンスを通常の文字と認識するプログラムでは、先頭に余分なデータがあるとみなされて問題となることがある。例えば、Unix系OSにおける実行可能スクリプトは、ファイル先頭が「#!」から始まるとき、それに続く文字列をインタプリタのコマンドとして認識するが、多くのシステムでは、このシーケンスが存在するとこの機能が働かず実行できない。PHPでは、<?PHP
の前に出力されるため、header()
関数の実行に失敗する原因となる。HLSLやGLSLのシェーダープログラムコンパイラ(fxcやglslangValidator)はBOMを処理できず、コンパイルエラーとなる。
逆にこのシーケンスがない場合、UTF-8と認識できないプログラムも存在する。たとえば、Microsoft Excelでは、CSVファイルを開くとき、このシーケンスが付加されていないUTF-8の場合は正常に読み込むことができない[要出典]。Microsoft Visual C++は既定でBOMなしUTF-8を認識せず、システムロケール設定に応じたマルチバイトエンコーディングとみなすが、Visual C++ 2015以降ではコンパイルオプションを指定することでBOMなしUTF-8を認識することができるようになっている[14]。
また、BOMがなくともエンコード自動推定によってUTF-8とShift_JISなどを区別することのできるプログラムもあるが、ASCII部以外の文字が少ない場合に誤認することが多い。
プロトコルが常にUTF-8であることを強制しているものである場合はこのシーケンスを禁止するべきで、この場合ファイル先頭にこのシーケンスが現れると “ZERO WIDTH NO-BREAK SPACE” と見なされる。逆にプロトコルがそれを保証しない場合このシーケンスは禁止されずファイル先頭のそれはバイト順マークと見なされる[15]。
Windows 10のメモ帳アプリは、2019年の19H1アップデートからBOM無しUTF-8がデフォルトになった[16]。
脚注
- ^ RFC 3629 UTF-8, a transformation format of ISO 10646
- ^ RFC 3629 Page-3
- ^ Rob Pike's UTF-8 history
- ^ ISO/IEC 10646:2003 Information technology -- Universal Multiple-Octet Coded Character Set (UCS)
- ^ RFC 2279 UTF-8, a transformation format of ISO 10646
- ^ The Unicode Standard, Version 5.2
- ^ RFC 3629 UTF-8, a transformation format of ISO 10646
- ^ ただし、バイト順マーク (BOM) が付加されている場合や、テキストを7ビットで処理するソフトウェア、内部的に最上位ビットを使用しているソフトウェアなど、使えないものも存在する
- ^ RFC 3629, pp.9f.
- ^ “10.1.10.6 The utf8mb4 Character Set (4-Byte UTF-8 Unicode Encoding)”. dev.mysql.com. MySQL 5.5 Reference Manual. Oracle. 2015年12月1日02:10:55時点のオリジナルよりアーカイブ。2015年12月11日閲覧。
- ^ Windowsにおける有名なワームであるNimdaウイルスは、IISにおけるUTF-8の脆弱性をもちいたものである。(はせがわようすけ 2009)
- ^ Mark Davis. “Forms of Unicode” (英語). IBM. 6 May 2005時点のオリジナルよりアーカイブ。18 September 2013閲覧。
- ^ このため、UTF-8という呼び名を使っていれば情報交換の相手が文書先頭にこのシーケンスがあると見なすと期待すべきではないし、また、UTF-8Nという呼び名は情報交換の際に用いるべきではない。
- ^ /source-charset (Set Source Character Set) | Microsoft Docs
- ^ RFC 3629 6. Byte order mark (BOM)
- ^ “「メモ帳」に多数の改善、BOMなしUTF-8がデフォルト保存形式に ~「Windows 10 19H1」”. Impress. 2023年1月26日閲覧。
参考資料
- 用語の日本語表記は原則として「“Unicode Terminology English - Japanese”. Unicode, Inc. 2010年1月1日閲覧。」にならった。
- はせがわようすけ (2009年5月8日). “本当は怖い文字コードの話: 第4回 UTF-8の冗長なエンコード”. 技術評論社. 2014年9月10日閲覧。