mozblog - もずぶろぐ -

自分のための備忘録や日記など。

文字コード体系まとめ

 エンジニアがいつかは立ち向かう必要がある命題、文字コードについて、私もそろそろきちんと理解しなくては、、、と思い、この本を読みました。

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)

プログラマのための文字コード技術入門 (WEB+DB PRESS plus) (WEB+DB PRESS plusシリーズ)

 

本だけでは少し足りないところをネットで調べ、本記事にまとめました。
そして分かったのは、文字コードの歴史というのは計算機の発展の歴史であり、企業の思惑や国際規格との競合の結果、本当に数奇な拡張を経てきたということでした。
同時に、ひとつに統一されればいいのになぁ、、、とも思いました。
ですが、過去のプログラムのエンコード方式が異なる場合など、単純に統一せずに拡張・互換性をもたせて運用していく姿勢が、今日まで続いています。

まとめるのに 10 時間以上かかって、長い記事になってしまいましたが、このような技術の発展の歴史は、概観していて本当に面白いですね。

ということで、下記よりまとめです。 

0. はじめに

文字コードについて理解するためには、まず、以下を区別する必要がある。

・符号化文字集合:コンピュータで利用する文字と、その文字に対して一意な番号を割り当てたもの。
・符号化文字方式:符号化文字集合をどのように表すか、バイト列 (ビット列) で定義したもの。

そして、文字種別の違いとして大きく 2 種類。

・制御文字:改行や、古いものだとベルを鳴らすような、端末に特殊動作をさせるための文字。
・図形文字:我々が取り扱う、いわゆる「普通の文字」。印刷可能な文字や表示可能な文字として定義されることもある。

日本語環境にて利用される符号化文字集合は大きく分けて 2 種類あり、ASCII からの流れをくむ ISO/IEC 2022 から発展した規格と、一般に Unicode といわれる ISO/IEC 10646 の規格がある。

 

1. 符号化文字集合

1-1. ASCII (1963 年)

7 bit で英数字を表す、最初期の文字集合で、米国の標準化組織である ASA (American Standards Association) が策定した。
符号化文字集合であると同時に、符号化文字方式でもある。
文字コードの初期設計では、世界中の文字を符号化するのではなく、米国の研究者が当面必要なコードだけをコード化しようとしたため、100 文字程度、つまり 7 bit で十分だった。
8 bit 目は通信のエラーチェックのためのパリティビットとして利用されていた。

1-2. ISO/IEC 646 (1967 年)

ASCII だけでは、アクセント記号など他言語で使われる、特殊な記号の付いたアルファベットが表現できないので、各国版の文字コードを、ASCII をもとにして作ろうとした。
そのための国際規格が ISO/IEC 646
ASCII の文字コードのうち、一部を他言語にあわせて変えて使えるようにしたもの。

1-3. JIS X 0201 (1969 年)

日本版の ISO/IEC 646 として、日本工業規格で定められたものが JIS X 0201
符号化文字集合でもあり、符号化文字方式でもある。
現存する JIS の文字コード規格の中では最も古いもので、7 bit でも、8 bit でも利用できる。

ラテン文字集合と片仮名集合を持ち、8 bit コードの場合は 第 8 bit が 0 の場合はラテン文字、1 の場合は片仮名文字として扱う。
7 bit の場合は、制御文字の SHIFT-IN (0x0F) もしくは SHIFT-OUT (0x0E) を呼ぶことで、両方の集合を切り替える。

ラテン文字集合は ASCII とは以下の違いしかない。

・符号位置 0x5C: ASCII ではバックスラッシュだが、JIS X 0201 では円記号

・符号位置 0x7E: ASCII ではチルダ だが、JIS X 0201 でオーバーライン (‾)

1-4. ISO/IEC 2022 (1973 年)

ISO/IEC 646 では複数の言語を同時に取り扱うことができなかったため、ASCII を拡張するための枠組みとして確立されたのが ISO/IEC 2022 で、世界中の文字コードの基礎となった。
ISO/IEC 2022 では、ISO/IEC 646 と同様の 7 bit コードも使えるし、8 bit コードも使える。また、複数バイトの文字列も規定されており、理論上は何バイトでもよいが、ほとんどは 2 バイトの規格として使われている。

7 bit コードの場合、128 文字を表すことができるが、0x00 から 0x1F までは制御文字領域、0x20 は SP (スペース)、0x21 から 0x7E までが文字、0x7F が DEL (削除文字)。

8 bit コードの場合、256 文字を表すことができるが、上記の 7 bit コードの構造に加え、0x80 から 0x9F までは制御文字領域、0xA0 から 0xFF までも文字が取り扱える。
0x00 から 0x1F までを CL 領域、0x20 から 0x7F までを GL 領域、0x80 から 0x9F までを CR 領域、0xA0 から 0xFF までを GR 領域という。

つまり、7 bit コードは CL 領域と GL 領域しかないコードとして取り扱える。

ただし、GL 領域や GR 領域では、SP や DEL のように、2 文字分を制御文字として使う場合があるため、実質は領域当たり 94 文字を表現できるが、この 94 文字がひとつの単位となる。

複数言語を同時に取り扱うときは、GL もしくは GR 領域に、他言語のテーブルを呼び出して使う。
他言語のテーブルは、複数バイト(たいていは 2 バイト) を用いて表現するため、94 * 94 = 8836 文字を表現できる。

呼び出すときは、ESC (0x1B) を用いて、エスケープシーケンスを挿入することで、テーブルを切り替える。
例えば、0x1B 0x28 0x4B という羅列を見つけると、ドイツ語にテーブルが切り替わる。

このように、理論的にはすべての言語を取り扱えるようにしたが、当時のコンピューターの処理能力や、記憶装置の容量といった問題もあり、世界中の文字を全て取り扱う必要があるといった需要が少なく、大きく普及はしなかった。
ASCII 互換なので、ASCII + 自国の文字集合、といった用途として使われることが多い。

1-5. JIS X 0208 (1978 年)

日本版の ISO/IEC 2022 として、日本工業規格として定められたもので、7 bit もしくは 8 bit を用いた 2 バイト文字コード
初版ののちに 3 回ほど改版されており、最新のもの (1997 年) では、ひらがな、カタカナ、漢字だけでなく、英数字やキリル文字、各種記号といった 6879 文字を収録している。

 8836 文字のうち、6879 文字が収録されているので、空き領域は自由に使える。
空き領域は機器ベンダが独自に定義する文字として利用されていることがあるが、これは環境依存文字といわれる。

 1-6. JIS X 0212 (1990 年)

JIS X 0208 に足りない文字(使用頻度が低いもの)を補うため、JIS X 0208 と組み合わせて使うことを前提とした文字集合
国文学研究資料館の研究に基づき、6067 文字が定義されているが、研究色が強く、一部の地理名などが表記できなかった。

JIS X 0208 と同様、ISO/IEC 2022 に準拠した 94 * 94 = 8836 文字の集合として定義されている。
そのため、JIS X 0208 を利用しているときに、エスケープ シーケンスを用いて呼び出される。

現在は、ほとんど利用されていない。

1-7. JIS X 0213 (2000 年)

 JIS X 0208 の上位互換で、JIS X 0212 があまり普及しなかったこともあり、「完成版の JIS X 0208」を策定する目的でつくられ、11233 文字を収録している。
通称 JIS 2000、あるいは 2004 年の改版後は JIS 2004 と呼ばれる。

94 * 94 = 8836 文字では足りないため、JIS X 0213 漢字集合 1 面と、JIS X 0213 漢字集合 2 面に分かれており、この二つをエスケープ シーケンス等で切り替えて使うことが想定されている。

新録された漢字の例では、SMAP草なぎ剛の「なぎ」の漢字などがある。
後述する Unicode でもこの漢字は収録されているが、今でも古い JIS 規格への対応のため、「なぎ」を平仮名表記しているメディアもある。

1-8. ISO/IEC 8859 (1987 年)

1987 年に初版、その後改版が進み、2009 年には ISO/IEC 8859-1 から ISO/IEC 8859-16 まで、廃棄された 12 を除き、15 部が存在する。

主にヨーロッパやアメリカで広く使われている文字コード

ASCII の上位互換として存在し、8 bit コードかつ 1 バイトで文字を表現する。
ASCII と互換性を保つため、GL 領域が ASCII になっており、GR 領域は各パートごとに文字表が定義される。

1-9. ISO/IEC 10646 (1993 年)

コンピュータの処理能力の増大を背景に、文字集合の組み合わせや切り替えを必要としないような、世界中の文字列をひとつの表に収めることを目的として開発開始。
ISO/IEC 2022 に基づいた各国のコードを平行移動するような形で、4 バイトで 1 文字を表そうとした。
各バイトをそれぞれ、群 (group)、面 (plane)、区 (row)、点 (cell) と表し、各面には ISO/IEC 2022 と同様に、0x20 から 0x7F までの GL 領域と、0xA0 から 0xFF までの GR 領域に、それぞれ文字を割り当てる。
ただし、2011 年には、利用されていないことから群は廃止された。

1990 年に国際標準の手前となる DIS (Draft International Standard) として策定された。
しかし、後述の Unicode が同時期に策定されており、同じ規格が 2 つ並立することを懸念し、最初の案は 1991 年 6 月の投票で否決された。
その後、UnicodeISO/IEC 10646 を互換性を持たせることになり、群 00 の面 00 に対して、Unicodeそっくりそのまま入れる構造にすることで、初版が確立された。

このような背景もあり、現在は Unicode とほぼ同義となっている。
Unicode に文字が追加されれば、ISO/IEC 10646 にも追加され、逆もまた同様とすることで、同期してアップデートされている。

1-10. Unicode (1993 年)

1980 年代にゼロックス社が提唱し、マイクロソフトやアップルなど、IT 企業が開発したもの。
目的は ISO/IEC 10646 と同様で、「世界中の文字を表す」ことを目的とした符号化文字集合

それぞれの文字は、U+FFFF のような形で、16 進数が割り当てられている。
制御文字は U+0000 から U+001F、U+007F、U+0080 から U+009F が割り当てられている。

当初は 16 bit (65536 文字) で全ての文字を符号化しようとしていたが、のちに不十分であったことが分かる。
それに伴い、当初の 16 bit で符号化したものと互換性を保つ形で、現在は 21 bit まで拡張されている。

1-11. JIS X 0221 (1995 年)

ISO/IEC 10646 の日本語版規格。
技術的に一致するように作られている。

 

2. 符号化文字方式

2-1. 漢字用 7 bit 符号 (JIS X 0208) (1978 年)

すべての文字が 2 バイト固定で、かつ 7 bit で表される。
7 bit で、最上位のビットは使わないことから、GL 領域のみを利用する。
JIS X 0208 で定義したコードポイントに、0x20 を足すことで、ビット符号化する。
これは、JIS のコードポイントが 1 から始まるが、1 をそのまま利用すると、制御文字領域 (CL) と被る。
そこで、図形文字領域に移動するため、0x20 を追加する。

2-2. Shift_JIS (JIS X 0208) (1982 年)

 マイクロソフトが中心となって策定されたもので、MS-DOS の標準日本語コードとして採択された。

JIS X 0201 の 8 bit 符号を元にしているため、8 bit 符号で、1 バイトと 2 バイトが混在する。
また、本来は制御文字の領域である、CR 領域を図形文字の領域として使う。

2 バイトの場合、1 バイト目 (0x8D 0xA1 なら 0x8D の方) として使える値は以下のとおりの 47 個。

・0x81 から 0x9F の間の値 (31 個)
・0xE0 から 0xEF の間の値 (16 個)

2 バイト目 (0x8D 0xA1 なら 0xA1 の方) として使える値は以下のとおりの 188 個。

・0x40 から 0x7E の間の値 (63 個)
・0x80 から 0xFC の間の値 (125 個)

よって、1 バイト目が 0x20 から 0x7F まで、もしくは 0xA0 から 0xDF までの値であれば、JIS X 0201 と判断し、それ以外であれば 2 バイト目も含め JIS X 0208 として取り扱う。
また、1 バイト目が 47 種類しか表現ができないので、JIS X 0208文字集合マッピングするのには、特殊な計算式を用いる。

JIS X 0201JIS X 0208 で、同じ文字を別の符号で表す箇所が出てくるため、その場合は以下のポリシーに従う。

JIS X 0201 ラテン文字集合と JIS X 0208 の両方に定義された文字の場合、JIS X 0201 の方を採用する。
JIS X 0201 片仮名集合と JIS X 0208 の両方に定義された文字の場合、JIS X 0208 の方を採用する。

結果的に、MS-DOS および Windows OS に搭載されたこともあり、JIS X 0208 準拠の符号化方式のなかでは、最も広く普及した。
エスケープ シーケンスを利用しない分、当時のコンピュータの処理能力では、文字を素早く処理できるといった利点があった。

・CP932 との違い

当初、マイクロソフトでは Shift_JIS に対して CP932 という管理番号が与えていたが、当時のマイクロソフトは、マイクロソフトの OS をインストールしたコンピュータを販売する企業に、CP932 の拡張を許していたため、CP932 の亜種が出回ることになる。

拡張を禁止したのは 1993 年だったが、この時点で主流であった NECIBM の拡張した CP932 を統合することで、新たな CP932 を作り、IANA に対して Windows-31J という名称で登録した。
これは Java では MS932 として呼称されている。

したがって、Windows-31 J と MS932 は Shift_JIS の上位互換として、いくつかの拡張文字列を含んでいる。

拡張方式である Shift_JIS-2004 にて、JIS X 0213 にも対応している。

2-3. EUC-JP (JIS X 0208) (1985 年)

EUC とは Extended Unix Code の略称。
1980 年代前半に、日本語 UNIX システム諮問委員会が UNIX 上で扱うための文字コードについて協議し、議論の結果をもとに、1985 年に AT&T によって定められた。

0x20 から 0x7F までの GL 領域は ASCII に固定し、0xA0 から 0xFF までの GR 領域は JIS X 0208JIS X 0201 の片仮名集合、JIS X 0212 のうちいずれかとなる。

8 bit で、1 バイトと 2 バイトが混在するが、1 バイトであれば ASCII もしくは制御文字となる。
2 バイトの場合は、0xA0 から 0xFF までの値を 2 つ並べる形で 1 文字を表現する。
そのため、JIS X 0208 で定義したコードポイントに 0xA0 を足したものが、ビット列となる。

制御文字 0x8E があると、直後の 1 文字は JIS X 0201 の片仮名集合として判定され、制御文字 0x8F があると、直後の 1 文字は JIS X 0212 として判定される。

特徴として、第 8 bit が 0 であれば ASCII として取り扱えるので、ASCII にしか対応していないシステムでも 8 bit 目が素通しであれば、EUC-JP でエンコードしていても、通過できるなど、ASCII 互換である。

また、ASCII と JIS X 0208 を併用することから、アルファベットなどは一意な符号化ができず、例えば A は 0x41 と 0xA3C1 という 2 種類の表現ができてしまうことになるが、その場合は ASCII の方式である 0x41 の方を採用するよう、定められている。

Unix Code という名の通り、Unix 上で利用されることが多い。

拡張方式である EUC-JIS-2004 にて、JIS X 0213 にも対応している。

2-4. ISO-2022-JP (JIS X 0208) (1986 年)

JUNET と呼ばれる、日本の研究用ネットワークで、ネットニュースや電子メールを日本語で送受信するために策定された。俗に JIS コードとも呼ばれる。

ASCII、JIS X 0201 ラテン文字集合、JIS X 0208 (1978 年版)、JIS X 0208 (1983 年版)  を混在させて使うが、実質は ASCII と JIS X 0208 (1983 年版) を切り替える。

7 bit で GL 領域のみを利用し、1 バイトと 2 バイトが混在する。
そのため、第 8 bit が 1 のコード範囲、0x80 以上は全く使用しない。
漢字用 7 bit 符号と同様に、JIS X 0208 のコードポイントに 0x20 を足すことでビット符号化する。

エスケープシーケンスで 4 種類の文字集合を GL 領域に呼び出し、切り替えて使う。
0x1B 0x24 0x42 を見つけたら、以降のビット列を JIS X 0208 (1983) として取り扱い、0x1B 0x28 0x42 の後は ASCII として取り扱う。
また、ファイル終端、もしくは改行 (0x0D 0x0A) の前には、状態が ASCII 以外であった場合、ASCII もしくは JIS X 0201 ラテン文字集合の状態に戻すためのエスケープ シーケンスを挿入する。

エスケープシーケンスがなければ ASCII のデータとなるので、ASCII 互換である。

7 bit の通信環境に適しているため、電子メールの通信で使われる。

拡張方式である ISO-2022-JP-2004 にて、JIS X 0213 に対応。

2-5. UCS-2 (Unicode) (1993 年)

UCS とは、Universal Multiple-Octet Coded Character Set の略称。
Unicode の符号化方式の最初期バージョンで、16 bit 固定で Unicode のコードポイントをそのままバイト列で表す。

16 bit の場合、65536 文字を収録できるが、世界中の文字を収めるには不十分であり、16 bit 固定であったために拡張性がないこともあり、普及しなかった。

2-6. UTF-8 (Unicode) (1993 年)

Unicode の一つのコードポイントを、1 バイトから 4 バイトまでの可変長で符号化する方式で、主流な Unicode の符号化方式の中では、唯一の ASCII 互換である。
1993 年に初版が提案された。

符号化は以下のとおり行う。

・U+0000 から U+007F: 0xxx xxxx で表す。
・U+0080 から U+07FF: 110x xxxx 10xx xxxx で表す。
・U+0800 から U+FFFF: 1110 xxxx 10xx xxxx 10xx xxxx で表す。
・U+10000 から U+10FFFF: 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx で表す。

5 バイト以上も一応定義されているが、2000 年には後述の UTF-16 で表現できる文字列を表すように制限されたため、事実上は 4 バイトまでとなる。

この符号化により、U+007F までが ASCII とビット列が一致する。
また、ビット列の先頭を見ることで、文字の区切りが明確に分かるようになっている。

UTF-8 は、このように先頭のビット列で判断するため、エンディアンの問題はない。
ただし、BOM (0xEF 0xBB 0xBF) は、UTF-8 のデータであることを明示するために、データの先頭に付与されることがある。

また、UTF-8 の亜種として、BMP 外の符号位置 (U+10000 から U+10FFFF) を UTF-16サロゲートペアで符号化し、さらにそれを UTF-8 に符号化したものが、一部プログラムの内部処理に使われていることがある。
Java のクラスファイルでは、Modified UTF-8 という形でこのような符号化が行われている。

現時点では最も普及している文字コードであり、特に Web ページの分野では、2018 年時点で 91 % ほどが UTF-8 で記述されていると言われている。

 2-7. UTF-16 (Unicode) (1996 年)

Unicode のバージョン 2.0 をもとに作られた、16 bit 単位の符号化形式。
上述の UCS-2 に「サロゲートペア」と呼ばれる概念を追加することで、より多くの文字を表現できるようにしたもの。

UnicodeBMP の文字は 16 bit の単位ひとつで表し、BMP の範囲では UCS-2 と互換性がある。
BMP 以外の面の文字、つまり U+10000 から U+10FFFF までの文字は、単位を 2 つ用いて表す。

BMP 以外の面の文字の場合は、サロゲートペアを使う。
サロゲートペアは、Unicode で文字が定義されていなかった U+D800 から U+DFFF までのコードポイントを表す。
また、U+D800 から U+DBFF を上位サロゲート、U+DC00 から U+DFFF を下位サロゲートとし、これを組み合わせて 1 文字を表現する。
例えば、0xD8 0x67 0xDE 0x3D というような、サロゲートペアを 2 つ組み合わせることで、1 文字を表せるようにしている。

また、バイトの並べ方によって、ビッグエンディアンUTF-16-BE と、リトルエンディアンの UTF-16-LE がある。
上位 8 bit が先にくるものがビッグエンディアンで、下位 8 bit が先にくるものがリトルエンディアンとなる。
例えば、U+6587 を表すときに 6587 のビット列で表現するのがビッグエンディアン、8765 のビット列で表現するのがリトルエンディアンである。
どちらの方式か判別するために、BOM (バイト順マーク) と呼ばれるビット列をデータの先頭に付与する場合があり、U+FEFF が BOM の符号にあたる。
先頭が FFFE であればリトルエンディアン、FEFF であればビッグエンディアンとして判定して、UTF-16 のビット列を解釈する。

Java の内部で利用されているなど、ファイルやネットワークでのデータ交換よりは、プログラムの内部処理で利用されていることが多い。

ちなみに、Windows のメモ帳で、ファイルの保存時に表示される Unicode とは、UTF-16 のことを表す。

2-8. UTF-32 もしくは UCS-4 (Unicode) (2002 年)

Unicode の符号位置をそのまま 32 bit で表現したもので、2002 年に Unicode のバージョン 3.2 にて定義されている。
そのため、1 つの単位は必ず 4 バイトとなる。

UCS-4 は、当時の理論上は群 00 から FF、かつ面 00 から FF までのすべての符号を利用できるように定義されていた。
UTF-32 は、そのうち群 00、面 00 から面 10 までしか使わないため、UCS-4 の部分集合と言えるが、UCS-4 でも、そのほかの群および面には文字が割り当てられていないため、同義と言ってよい。

UTF-16 と同様に、バイト順によりビッグエンディアン、リトルエンディアンがあり、BOM も使われる。

表現する文字列のほとんどが英数字である場合、必ず 4 バイトになる性質から、ほとんどのバイトが 00 になり、記憶領域を無駄にすると考えられることもある。
プログラムの内部処理で、様々な文字列を処理する必要がある場合に利用されることが多い。