$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

符号化文字集合と文字符号化方式 - 「プログラマのための文字コード技術入門」を読んだ

最近文字コード周りでハマった時に、文字コードというものをそもそもちゃんと分かってないと気づいた。そこで「プログラマのための文字コード技術入門」を読んで勉強してみた。

この本はコンピュータにおける文字について体系的にまとめてくれている。ASCIIやLatin1、UTF-8Unicodeについて、どういう分類で何のために使われているかを知ることができる。また、歴史も合わせて教えてくれるので、今現状起こっている問題(円マークとバックスラッシュ問題とか)がどういう経緯で起こってしまったのかなどを理解できる。

簡単な文章で書かれているので、一度でも文字コードに関係することでハマったことのある人なら、どんどん読めると思う。ただ、符号化文字集合文字符号化方式の違いについてわかった上で読んだほうが内容が理解しやすいと思う。

個人的な感想では、この本によって今まで漠然としか分かってなかったことが明確にできたので、とにかく良い本だった。過去になぜハマったのかとか、よく分からずに使っていたこととかを理解することが出来た。おすすめ。


この本の中で、自分は以下の二つが特に勉強になったのでまとめてみる。

符号化文字集合文字符号化方式

これまで自分はコンピュータの中の文字というものを、「文字コード」という一つの概念として捉えてしまっていた。しかし、実際には符号化文字集合文字符号化方式という二つの概念が存在し、これらが組み合わさってコンピュータの文字というものを表しているということを知った。

符号化文字集合

まず符号化文字集合について。文字を表現するためには、まずは文字の集合が必要である。またコンピュータでそれを表すためには、それぞれの文字に一意な番号をふって置く必要がある。例えば、スペースなら32番、「a」なら97番という感じ。番号をふっておけば、その番号に従って、ディスプレイ上に文字を表示することができる。この番号のことを、便宜的にこの記事では「コードポイント」と呼ぶ。

JIS X 0201-1997規格票によると、符号化文字集合の定義としては以下のとおり。

符号化文字集合、符号文字集合を定め、かつ、その集合内の文字とビット組合せとを1対1に対応づける、あいまいでない規則の集合。

ASCIIやJIS、Unicodeなどは符号化文字集合である。

文字符号化方式

次に文字符号化方式について。文字符号化方式は、複数の符号化文字集合を組み合わせる、過去への互換性を保つ、などといった要件を達成するための運用方式のこと。符号化文字集合を運用しやすいように別のバイト列に変換する方式のことである。

EUC-JPや、Shift_JISUTF-8UTF-16などは文字符号化方式である。例えばJIS系の文字集合を符号化する方法がEUC-JPやShift_JISであり、Unicode文字集合を符号化する方法がUTF-8UTF-16である。

符号化文字集合文字符号化方式の関係

符号化文字集合文字符号化方式の関係は図にしてみると分かりやすい。イメージ図のため少し正確さにはかけるが、こういうイメージ。

f:id:shiba_yu36:20150913141538j:plain:h500

上のように「あ」を例にしてみる。「あ」はUnicodeのコードポイントでは「U+3042」である。これをUTF-8で符号化した場合、「0xE3 0x81 0x82」というバイト列になる。これをUTF-16で符号化した場合は、バイト列はUTF-8と違い、「0x30 0x42」となる。

JISとEUC-JPもこのような関係になっている。Unicodeは世界中の文字を一つの文字集合にまとめるという思想なので、文字集合は一つになっている。EUC-JPの場合はASCIIとJIS X 0208JIS X 0201JIS X 0212の4つを文字集合を扱うので、この切替もEUC-JPという符号化方式によってなされている。

なぜ自分が混同してしまったか

自分は符号化文字集合文字符号化方式を混同して、「文字コード」という概念として考えてしまっていた。なぜこうなってしまったのか考えてみることで、もうすこし文字について理解を深めてみたい。


考えてみたところ、それはASCII、Latin1、UCS-2の存在があるからなのではないかと思った。

ASCIIの時代は文字集合が128個以下に収まっていたため、コードポイントは7bitまでで収まっていた。そこで1バイト単位で考えて、何も変換せずともそのコードポイントのまま扱ってしまえば、問題なかった。Latin1の場合も文字集合は256個以下に収まっていたので、そのコードポイントのまま扱ってしまえばよかった。つまりそのころは文字符号化方式という変換方式は必要なかった。

ASCIIやLatin1はコードポイントと符号した後のバイトが一致しているので、現在でいうところの符号化文字集合文字符号化方式の両方の役割をまとめておこなっているように見える。

このことや文字コードを指定する際にLatin1とEUC-JP、UTF-8を選べることから、自分の中ではASCII、Latin1、EUC-JPUTF-8ASCIIもLatin1もUTF-8も同じように考えてしまって、すべて混同してしまっていたのではないかと思う。


ちなみに、ASCIIやLatin1で保存されたファイルが、コードポイントと一致しているかは、以下のコマンドで出力される16進数が、https://ja.wikipedia.org/wiki/ASCIIhttps://ja.wikipedia.org/wiki/ISO/IEC_8859-1 に書いてあるコードポイントと一致しているかを見れば分かる。

$ od -c -t x1 text-ascii-containing-only-ascii.txt
$ od -c -t x1 text-latin1-containing-all-latin1-characters.txt

https://github.com/shibayu36/char-encoding-study この辺に適当にファイルとかは置いておいた。

ある文字符号化方式がASCIIに互換しているとは

たまに、「ASCII互換の場合は」という言葉を見かけることがある。自分はこれについてよく分かってなかったのだけど、この本を読んで理解することが出来た。

自分の理解では、ある文字符号化方式がASCIIに互換しているとは、「ASCIIで保存されたASCIIの文字集合のみで構築された文章をその文字符号化方式で読み込んだ時に、同じ文章を表示できるか」、である。


例えば「aiueo」という文字列について考えてみる。「aiueo」はASCIIで保存されると「0x61 0x69 0x75 0x65 0x6f」というバイト列として保存される。この「aiueo」をUTF-8で保存すると、同様に「0x61 0x69 0x75 0x65 0x6f」として保存される。

このようにASCIIとUTF-8はASCII文字集合の文字列を同じバイト列で保存しているため、ASCIIで保存された文章をUTF-8で読み込んだ時に同じ文章を表示できる。よってUTF-8はASCIIに互換している。

UTF-16の場合は「aiueo」は「0x00 0x61 0x00 0x69 0x00 0x75 0x00 0x65 0x00 0x6f」というバイト列になる。そのためASCIIで保存された「0x61 0x69 0x75 0x65 0x6f」というバイト列をUTF-16で読み込んでも、「aiueo」とは解釈できないため、UTF-16はASCIIに互換していない。


昔に書かれたプログラムなどはASCIIで書かれている。これとの互換性を持たせないと古い資産を一度今の文字コードに変換してからでないと利用することが出来ない。UTF-8の場合、ASCIIに互換しているため、古いプログラムもそのまま利用できる。これがUTF-8が今一般的になっている理由の一つかなと思った。

まとめ

プログラマのための文字コード技術入門」は、これまで「文字コード」という概念としてなんとなく知っていたものを明確にしてくれる本で非常に良かった。一度でも「文字コード」周りでハマったことがあるなら読んでみると良さそう。

他にも以下のようなURLが参考になった。