文字コードについての予備知識 - ユニコード編
《 初回公開:2022/12/24 , 最終更新:2023/01/26 》
以前の記事と重複する部分もあるが、今回はユニコードに的を絞ってまとめてみる事にする。
あっちこっちのネット記事の引用だらけであるが詳しくは引用先のサイトを参考に。
【 目次 】
ユニコードは、アップル、ヒューレットパッカード、IBM、マイクロソフトなど、多くのコンピューター関連企業が中心となって設立された団体、ユニコードコンソーシアムによってつくられた文字コード体系です。
文字コード
文字コード(もじコード)は、コンピュータ上で文字(キャラクタ)を利用する目的で各文字に割り当てられるバイト表現。
もしくは、バイト表現と文字の対応関係(文字コード体系)のことを指して「文字コード」と呼ぶことも多い。
文字コードを以下のの2段階に区別する場合がある。
- 文字集合(文字セット)
- 符号化方式(エンコーディング)
文字セット(文字集合) - 符号化文字集合 Coded Character Set(CCS)
文字と一意に振られた番号のペアの集合。
文字に番号を付けて表現する。
文字セットには以下のようなものがある。
- ascii
- Unicode
- JISX0208
- JISX0213
コードポイント
コードポイント(符号点)は文字に割り当てられた番号の事
コードポイントは、 (Unicode などの) テキストを表現するシステムにおいて、抽象文字を表現するために割り当てられた番号のことです。
Unicode では、コードポイントは、 "U+1234" という形式で表現されます。
ここで「1234」が割り当てられた番号です。例えば、 "A" という文字には、 U+0041 というコードポイントが割り当てられています。
符号点(ふごうてん)は、符号化文字集合内の、文字を割り当てうる個々の点。
コードポイント (code point)。Unicodeでは符号位置(ふごういち)と訳す。文脈によっては単に点(てん、point)ともいう。
ユニコードのコードポイントは「U+0000~U+10FFFF」の範囲。
Unicode策定当初は2バイト(U+0000~U+10FFFF)で表現できる文字を規定していたが、それでは文字がおさまりきれずは4バイトに拡張された。
群・面・区・点
0000~10FFFF の 17×256×256 = 1114112 に拡張されました。
符号位置は4桁以上の16進数で U+20B9F のように表します。
6桁の16進数で表したときの最初の2桁を「面 (plane)」、次の2桁を「区 (row)」、最後の2桁を「点 (cell)」と呼びます。
...
32ビットを想定して、「群 (group)」、「面」、「区」、「点」という概念を定義しましたが、Unicode と同一になったことで、「群」は廃止されました。
...
17面あるうちの最初の第0面を 基本多言語面 (BMP: Basic Multilingual Plane)、それ以降の面を追加面 (Supplementary Planes) と言います。日本語関連では、JIS X 0201、JIS X 0208、JIS X 0212 の収録文字はすべて BMP の範囲で表現できますが、JIS X 0213 の収録文字の一部は、BMP の追加申請に間に合わなかったため、追加面に登録されており、16ビット単位で表現する場合 (UTF-16)、サロゲートペアという仕組みを用いることになります
第0面を 基本多言語面 (BMP: Basic Multilingual Plane) - U+0000~U+FFFF
1面~16面(0x01~0x10)を 追加面 (Supplementary Planes) - U+10000~U+10FFFF
「群」は廃止。
- 1992年 Unicode 1.0.1 - CJK統合漢字の導入。
- 2013年 Unicode 6.3 - CJK互換漢字の SVS 登録
範囲 文字ブロック 日本語関連 U+0000~U+007F C0制御文字と基本ラテン文字 US-ASCII と同一 U+0080~U+00FF C1制御文字とラテン1補助 ISO-8859-1 と同一 U+0100~U+036F (複数ブロック) 修飾付きラテン文字、修飾文字等 U+0370~U+03FF ギリシア文字 ギリシア文字 U+0400~U+04FF キリル文字 キリル文字 U+1E00~U+1FFF (複数ブロック) 修飾付きラテン文字、ギリシア文字 U+2000~U+206F 一般句読点 記号等 U+20A0~U+20CF 通貨記号 ユーロ記号 U+2100~U+24FF (複数ブロック) 記号、ローマ数字、丸付き英数字等 U+2500~U+257F 罫線素片 罫線素片 U+25A0~U+29FF (複数ブロック) 記号 U+3000~U+303F CJKの記号と句読点 句読点等 U+3040~U+309F 平仮名 平仮名、濁点・半濁点 U+30A0~U+30FF 片仮名 片仮名 U+31F0~U+31FF 片仮名拡張 小書き片仮名 U+3200~U+32FF 囲みCJK文字・月 括弧付き漢字、丸付き数字等 U+3300~U+33FF CJK互換用文字 単位、元号等 U+3400~U+4DBF CJK統合漢字拡張A 拡張漢字 U+4E00~U+9FFC CJK統合漢字 漢字 U+D800~U+DFFF サロゲート領域 サロゲートペア U+E000~U+F8FF 私用領域 ユーザー外字 U+F900~U+FAFF CJK互換漢字 IBM拡張漢字、拡張漢字 U+FE00~U+FE0F 異体字セレクタ 標準異体字シーケンス (SVS) U+FE30~U+FE4F CJK互換形 記号 U+FFF0~U+FFEF 半角・全角形 半角片仮名、全角英数字等 U+FFF0~U+FFFF 特殊記号 置換文字、BOM U+20000~U+2A6DD CJK統合漢字拡張B 拡張漢字 U+E0100~U+E01EF 異体字セレクタ補助 漢字異体字シーケンス (IVS)
CJKあるいはCJKV
CJK統合漢字
CJK統合漢字(シージェーケーとうごうかんじ、英: CJK unified ideographs)は、ISO/IEC 10646(略称:UCS[1])およびUnicode(ユニコード)にて採用されている符号化用漢字集合およびその符号表である。CJK統合漢字の名称は、中国語(英: Chinese)、日本語(英: Japanese)、朝鮮語(英: Korean)で使われている漢字をひとまとめにしたことからきている。
CJK統合漢字の初版であるUnified Repertoire and Ordering第二版は1992年に制定されたが、1994年にベトナムで使われていた漢字も含めることにしたため、CJKVと呼ばれることもある。CJKVは、中国語・日本語・朝鮮語・ベトナム語(Vietnamese) を表す英語の頭文字である。特にその四つの言語で共通して使われる、または使われていた文字体系である漢字(チュノムを含む)のこと。ソフトウェアの国際化、中でも文字コードに関する分野で用いられる。
CJK統合漢字は、中国・台湾・日本・北朝鮮・韓国・ベトナムの各国・地域の工業規格で定められた漢字コードとの対応表も定めているが、事情によりCJK統合漢字との対応を持たない各国・各地域の漢字コードをUCSに適切に変換できるよう、互換用の領域が別途定められている。この領域の漢字はCJK互換漢字[2]と呼ばれる。
- 中国語
- 日本語
- 朝鮮語
CJKV
CJKV は、中国語・日本語・朝鮮語・ベトナム語 (英: Chinese-Japanese-Korean-Vietnamese) の略。特に、その四言語で共通して使われる、または使われていた文字体系である漢字(チュノムを含む)のこと。ソフトウェアの国際化、中でも文字コードに関する分野で用いられる。
比較的早くに漢字を廃止し、漢字に含めるべきか諸説あるチュノムを擁するベトナム語を除いた中国語、日本語、朝鮮語の頭文字だけをとって CJK と呼ぶこともある。主な東アジアの書記系を総称するときに用いられる。用語の使用頻度は CJKV より CJK のほうが多いが、CJK と言いながら実際は CJKV について述べていることも多い。
- 中国語
- 日本語
- 朝鮮語
- ベトナム語
CJK と言いながら実際は CJKV について述べていることも多い。
CJKの実態は CJKV
漢字検索
UCS
ユニコードの文字セットはUCS(Universal Multiple-Octet Coded Character Set)と呼ばれる。
UCS-2 -> 2バイトのユニコードの文字セット
UCS-4 -> 4バイトのユニコードの文字セット
UCS-2
UCSの中2バイトの値として表現できる第0面,基本多言語面BMPの部分の文字セットを指す。
世界の主要な言語で使われる文字のほとんどが含まれる。
UCS-4
UCS-2を含めてUCSのすべての文字が含まれる4バイトのユニコードの文字セット
文字エンコーディング - 文字符号化方式(CES,character encoding scheme)
「文字エンコーディング」は、文字に振られた番号をバイト表現に変換する方法。
「文字コード」とほぼ同意である。
コンピュータがデータを扱う場合、バイト列を使って処理をすることになる。
プログラミング言語で文字列を内部に格納したり、ファイルに文字列データを保存あるいはネットワーク等の入出力ストリームに転送する場合、文字エンコーディングされて転送される事になる。
UTF
UTF (UCS Transformation Format もしくは Unicode Transfer Format)とは、ユニコードの文字列UCSを、コンピュータが扱いやすいようにバイト列に変換する方式
ユニコードの文字エンコーディングには使用されるbit単位によって以下のものがある。
- UTF-7 7 bit単位
- UTF-8 8 bit単位
- UTF-16 16 bit単位
- UTF-32 32 bit単位
UTF-7
7ビットでしか送信できない制限があるプロトコル上のメールやニュースなどの環境で、その体系上でUnicodeのメールを送信可能にするために作られた規格である。
現在では正しく実装されていないアプリケーション上でセキュリティー上の脆弱性を発生させることがあることから、あまり使われなくなっている。62のアルファベットと9の記号(' ( ) , - . / : ?)はそのまま表記する。 それ以外の文字はUTF-16のビッグエンディアンで符号化し、修正BASE64で符号化する。[1] BASE64の文字の前に「+」後ろに「-」を置く。 「+」の文字自体は「+-」で表現する。
Base64は、データを64種類の印字可能な英数字のみを用いて、それ以外の文字を扱うことの出来ない通信環境にてマルチバイト文字やバイナリデータを扱うためのエンコード方式である。 MIMEによって規定されていて、7ビットのデータしか扱うことの出来ない電子メールにて広く利用されている。
A、…、Z、a、…、z、0、…、9 の62種類の文字と+、/、さらにパディング(余った部分を詰める)のための記号として = が用いられる。 MIMEの基準では76文字ごとに改行コードが入る。
変換形式
元データを6ビットずつに分割する。(6ビットに満たない分は後ろに0を追加して6ビットにする)
各6ビットの値を変換表を使って4文字ずつ変換する。
4文字に足りない分は = 記号を後ろに追加する。
UTF-8
8ビットの可変長マルチバイトで文字を表現
長さが1 ~ 4bytesの可変長の符号化方式
UTF-8は英数は1バイトで表現し、日本語は3バイトで表現するようになっています。
英数の割合が多い場合はUTF-8の方が効率が良い
UTF-8を標準として使用することが多くなっています。
DNSなどの文字列にはUTF-8を使用
2最も頻繁に使われる(U+0000 ~ U+007F)の文字(ASCII文字/半角英数字)は1byteに収まり、コード効率が高い
ラテン圏でよく使われる文字範囲(U+0080 ~ U+07FF)は2bytesで済み、コード効率が良い
基本的な漢字はほぼ基本多言語面(BMP)(U+0000 ~ U+FFFF)に収容されており、ほとんどの日本語文字は3bytes
1「あ」の番号は16進数で3042
2進数にすると、0011000001000010
先頭の0を除いたビット数は14
7ビット以下なら、そのまま使用→ASCII文字は変換せずそのまま使用される
下位ビットから6ビット単位で分割
11 000001 000010
最上位以外に次のように10を付加して8ビットにする
11 10000001 10000010
最上位バイトの先頭(8ビット目)から、最上位バイトを含めたバイト数個のビット1と、0を付加
11100011 10000001 10000010
4bit-6bit - 6bit
0011-000001- 000010
11-10000001- 10000010
UTF-16
16ビットの可変長マルチバイトで文字を表現
2バイト or 4バイト
日本語が多い場合はUTF-16の方が効率が良い
Unicodeの符号位置(コードポイント)を16bitで表現する方式
U+0000 ~ U+FFFFの範囲ならそのまま16bitデータとして格納 ⇒ 2byte
U+10000 ~ U+10FFFFの符号位置(コードポイント)はサロゲートペアを使って表現 ⇒ 4byte
サロゲートペア
2サロゲートペア(surrogate pair:代用対)とは、Unicodeに第1面(SMP)以降を追加したときに導入された新しい符号化方法です。
16bit幅の符号位置(コードポイント)を2つ使い、U+10000 ~ の文字を表現します。
基本多言語面(BMP)の未使用領域にあった符号位置(コードポイント)の内、
U+D800 ~ U+DBFF(1024文字分)- 上位16ビットに使用
U+DC00 ~ U+DFFF(1024文字分)- 下位16ビットに使用
これらのペアを使って、1024 × 1024 = 約100万文字分のコード領域を確保し、これを U+10000 ~ U+10FFFFに割り当てました。
ユニコード 一覧 D000-DFFF
- Unicode一覧 D000-DFFF - Wikipedia
D800-DFFF は代用対(サロゲートペア)に使われる。Unicode および ISO/IEC 10646 ではこの範囲の符号位置に文字を割り当てておらず、代用対に使う符号値は単独で文字を表すものではない。UTF-16 において、はじめに上位代用符号位置 (D800-DBFF)、次に下位代用符号位置 (DC00-DFFF) の符号位置の組み合わせにより、追加面の文字を表す。
2サロゲートペアへの変換は、
U+10000 ~ U+10FFFF
符号位置(コードポイント)からU+10000を減算
0 ~ 1FFFF
20bitの数値に変換
0000 ~ 1FFFF
それを上下10bitずつに分割
0 0 ~ 0001111111(7F) 1111111111(3FF)
それぞれの値に
U+D800
D800 ~ D87F
U+DC00
DC00 ~ DFFF
を加算
という手順を踏みます。
※D800-DBFFの範囲ならサロゲートペアの上位バイト,DC00-DFFFならサロゲートペアの下位バイトと判断できるという事かな。
オンラインの「ビット計算 ツール」を使って計算すると便利かな。
UTF-32
UTF-32は、サロゲートペアを使わずに、常に32bitでUnicodeの符号位置(コードポイント)を表現する符号化方式です。
2基本多言語面(BMP)以外のUnicodeの符号位置(コードポイント)が使われることは非常に少ないため、実際にはほとんどの場合は上位 16bitがゼロになり、メモリの利用効率はあまり良くありません。
固定長1文字32bitで処理できるためプログラミング状は扱いやすい
しかし、ほとんどの場合は上位 16bitがゼロになり、メモリを無駄に浪費。
UTF-8は
2ファイルへの保存や通信等では便利
メモリの利用効率も高い
ですが、1byte単位の可変長データなのでプログラムから操作するには不便です。
そこで、固定長データとして符号化する
UTF-16
UTF-32
の符号化方式が用意されました。
コードユニット - Code Unit(符号単位)
Unicodeでは1文字を表すのに使う最小限のビットの組み合わせをコードユニットという呼び方をする事がある。
コードポイントが2byteの32bit単位であるのに対して、
JavaScriptでは文字列の内部表現はUTF-16で、コードユニットは16bit単位という事になる。
JavaScriptのStringクラスのメソッドにはコードユニット単位(16ビット単位)で処理をおこなうメソッドが多い。
JavaScriptのメソッドんついては「JavaScriptと文字コード そして Unicodeとサロゲートペアの扱い - 愚鈍人」を参照。
ところで、コードページというキーワードも存在するがこれはベンダー独自に定められたものであり、ユニコードでは意味をなさない。
エンディアンとBOM(バイトオーダーマーク - Byte Order Mark)
コンピュータのCPUのタイプによって複数バイトで構成されるデータの処理に下位バイトが先になるリトルエンディアンと上位バイトが先になるビッグエンディアンが存在していて、異なるコンピュータ間でのデータのやりとりにおいてBOMというデータを付加する事でどちらのエンディアンかを示す事ができる。
UTF-8
UTF-8でのBOMの使用は非推奨。
本来、UTF-8はバイト単位なのでエンディアンの区別は必要なくてBOMも必要ないはずであるが、WindowsのアプリケーションによってはBOMによってShift-JISかあるいはUTF-8かUTF16,UTF32かどうかを判断するものがある。
BOM付きのUTF-8であれば先頭の3バイトがBOMであり、<0xEF 0xBB 0xBF>というデータになります。
...
Microsoft ExcelなどのアプリケーションによってはBOM付きでなければ符号化方式がUTF-8なのかUTF-16なのか、またはUTF-32なのか、あるいはまったく別の文字コードなのか判断できないことがあります。
一方、Webページとして使用されるHTMLファイルの場合はBOM無しで保存・上書きする方が良いとされています。
これはWebページを動的に処理するPHPなどのプログラムがBOM付きのテキストファイルを正常に処理することができないことがあるからです。このようにシチュエーションによりどちらの方が良いという事は一概には言えません。
UTF-8はエンディアン方式の違いはないので本来BOMは必要ありません。
しかし、アプリ(プログラム)によっては文字コードをUTF-8と判断できず、文字化け等の不具合を起こすことがあります。
※ Microsoftのアプリ(Excel、メモ帳)で多い ... BOM付きを想定されていないアプリ(プログラム)では、不具合を起こす場合があります。
※ Webページの開発で使われるhtmlやPHPではBOM無しでないと動作しない場合がある ... MicrosoftのExcelではBOM無しでは文字化けしてしまいます。
BOMが無いUTF-8をUTF-8Nと呼ぶこともある。
UTF-16
BOM無しビックエンディアン UTF-16BE
BOM無しリトルエンディアン UTF-16LE
BOM付きビッグエンディアンのUTF-16BE ⇒ BOMはFF FE
BOM付きリトルエンディアンのUTF-16LE ⇒ BOMはFE FF
UTF-32
- UTF-32LE
- UTF32BE
BOM付きビッグエンディアンのUTF-32BE ⇒ BOMは00 00 FE FF
BOM付きリトルエンディアンのUTF-32LE ⇒ BOMはFF FE 00 00
Unicode文字符号化モデル
文字コードは、Unicode文字符号化モデル[4]によると以下の4段階に分けられる:
- 抽象文字集合 (ACR)
符号化の対象とする順序のない文字の集合。- 符号化文字集合 (CCS)
抽象文字集合を非負整数に対応させたもの。この非負整数の範囲を符号空間、各値を符号位置 (コードポイント) といい、抽象文字は対応後、符号化文字となる[5]。抽象文字は複数の符号化文字に対応されることもある[6]。- 文字符号化形式 (CEF)
符号化文字集合の非負整数を符号単位列に変換する方法。文字符号化形式はコンピュータ中に実際にデータとして文字を表現することを可能にする。- 文字符号化方式 (CES)符号単位列をバイト列に直列化する方法。符号単位が8ビットより大きい場合はエンディアンが関係する。
その後、バイト列を、 gzip などで圧縮したり、7ビット伝送路に通すために Base64、Quoted-printable などで変換することがあるが、これらは文字コードの範囲外である。
文字符号化形式(CEF)と文字符号化方式(CES)
文字エンコーディングには文字符号化形式 (CEF - character encoding form)と文字符号化方式 (CES - character encoding schem) との区別があって
CEFはプログラムにおける文字の内部表現、
そしてバイト単位ではないのでエンディアンの区別がない。
CEFは、このUnicodeスカラ値を符号単位列に変換する。
UnicodeのCEFには、UTF-8・UTF-16・UTF-32があり、これらが使う符号単位はそれぞれ、符号なし8ビット整数[2]・符号なし16ビット整数・符号なし32ビット整数である。
これらの文字符号化形式はプログラムにおける文字の内部表現として実装される。
CEFは以下の3種類
- UTF-8
- UTF-16
- UTF-32
CESはバイト単位で処理に、 バイト単位ではエンディアンの区別が必要に。
しかし、情報交換のためファイルの読み書きや通信を行う場合には、符号単位列をバイト列にする必要があり、8ビット以外の符号単位列をバイト列にするには、バイト順序(エンディアン)を決める必要がある。
そのためCESでは、CEFに加え、バイト直列化の手続きを決める、つまり、エンディアンを指定するかまたはエンディアンを記述する方法を規格化している。Unicodeでは文字符号化方式としてUTF-8、UTF-16、UTF-16BE、UTF-16LE、UTF-32、UTF-32BE、UTF-32LEの7種類が定められている。
符号化方式が指定されずBOMも付与されない場合、ビッグエンディアンとして扱うと決められている。
文字符号化方式(CES) エンディアン BOMの付与 UTF-8 N/A 可 UTF-16 ビッグ/リトル 可 UTF-16BE ビッグエンディアン 不可 UTF-16LE リトルエンディアン 不可 UTF-32 ビッグ/リトル 可 UTF-32BE ビッグエンディアン 不可 UTF-32LE リトルエンディアン 不可
UTF-8でのBOMの使用は非推奨
CESは以下の7種類
- UTF-8
- UTF-16
- UTF-16BE
- UTF-16LE
- UTF-32
- UTF-32BE
- UTF-32LE
だけれど、UTF-16とUTF-32は更にBOM付きとBOM無しのものがあって、BOM無しの場合はビッグエンディアンとして扱うのかな。
プログラミング言語の内部表現
プログラミング言語の内部表現には何が使われているかと言うと。
Unicode登場初期に生まれたプログラミング言語では、文字列の内部表現がUTF-16になっているものが多い。C#もそうなっている。
一方で、1990年代の資産があまり関係ないプログラミング言語、特にWebでよく使われるものは、文字列の内部表現にUTF-8を使っていることが多い。C#などのUTF-16な言語でも、UTF-8を直接取り扱える方法が求められている。
ところで,プログラミング言語には,文字列の内部表現のシェアが大きく分かれている.大きくは,
- UTF-8 / UTF-16 / UTF-32 のいずれかを内部表現として使っている言語
- Unicode コードポイントの列を内部表現として使っている言語
に分かれている.個人的に観測している範囲では,古い言語は UTF-16 の採用率が高く,最近は UTF-8 の採用率が高い気がする.古い言語で UTF-16 採用率が高いのは,やはり Unicode の歴史的事情が大きく影響を与えているんじゃないだろうか? まあ,その辺の話は置いておいて,今回注目したいのが「Unicode コードポイントの列を内部表現として使っている言語」だ.例としては,Python,Haskell [5] が相当する.
C#やJavaScriptの内部表現はUTF-16,PythonはUTF-32のようで。