『プログラマのための文字コード技術入門』を読んだ

圧倒的な雨により、引きこもりを余儀なくされていた。
Amazon・プライム・ビデオで『ソラニン』という映画をみた。

ソラニン

ソラニン

青春の眩しさで目が潰れそうだった。
全フレームの全ピクセルで宮崎あおいが可愛かった。
桐谷健太に泣かされたし、ARATAが良かった。

どんな本か

本題です(本だけに、ってね!)。

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

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

文字コード周辺の技術や歴史をまるっとまとめたかんじの本。「入門」とあるが表面的な内容だけでなく、かなり深い説明もある。

人気っぽい。

なぜ読んだか

文字コードをいまだに寸分も理解できておらず、使いこなせていないため。都度インターネットを探索して調べるが、断片的だったり情報が古かったりして要領を得ないので、体系的に学んで全体像をつかみたいと思っていた。

文字コードを体系的にまとめた本は少なく*1、インターネットを調べると、この本と文字コード「超」入門が挙がってくる。ひとまず信頼と実績のWEB+DB PRESS plusシリーズを買ってみた。

感想など

タイトルに「入門」とあり、もちろん基礎的な話から書かれているが、かなり奥深い話題も丁寧に書かれていた。記憶にない用語がたくさん出てきて、2日で読み切るには脳がオーバーフローする密度だった。実際、かなり読み飛ばした。

とにかく用語を把握しながら読み進めるのがしんどかった。読んでる途中でJIS X 0201ってなんだったっけ…?とか。日頃の不徳の致すところです。

読む前と比べると、UnicodeとUTF-8の違いは?とか、符号化文字集合と符号化形式の違いは?とかの質問に対してはそれぞれのざっくりとした説明を加えて応えられる状態になったと思う。それぞれの仕組みを記憶するところまでは至っていない。今後精進したい。

おもしろかったところ

全部本筋とは違う。

幽霊漢字

78JIS制定の際に、何の字かわからない「幽霊漢字」を退治するため、古資料などを漁って漢字の身辺調査が行われた。結果、そのほとんどは地名や人名に使われている文字とわかった。
その際、何の字かまったくわからなかったのが、唯一、「彁」という文字。過去に誰かが間違えて書いたり、適当に書いたりしたものと推測されているが、手がかりがない。逆に、それ以外のすべては何らかの手がかりが見つかったのすごい。
また、「妛」という文字は、「山」と「女」を組み合わせた文字だったはずが、紙を切り貼りするときの切れ目で横線が入って誤認された文字。JIS X 0213では元の文字も収録された。
このあたりは*2などが詳しいらしいので、ご興味ある方はどうぞ。

アイヌ語

JIS X 0213で標準化されたアイヌ語。アイヌ語は文字を持たず、文芸は口伝えで伝承されてきており、その発音は日本語では表現しきれなかった。例えば、「セ゚」「ツ゚」「ト゚」など。音としては、「セ゚」は「ツェ」と同じ音らしい。

Youtubeを漁ればアイヌ語の音声が聴ける。日本語っぽく聞こえるのかと思っていたが、実際にはまったく日本語っぽさがなく、モンゴルのほう?の言葉っぽく聞こえた。余談すぎる。

円記号問題解決のための思考実験

円記号問題というのは、ASCIIとJIS X 0201ラテン文字との差異により、日本の通貨の円記号(¥)とバックスラッシュ()が入れ替わることがある現象。だれしもが経験してきているはず。問題の根本原因は、ASCIIではバックスラッシュ、JIS X 0201では円記号を表す0x5Cというコード値を、同一のものとして扱っていること。

これを解決するには、0x5Cの意味の違いを厳密に運用する。Shift_JISで符号化されたプログラムでに print("\n") と書かれていれば、それは改行文字ではなく、円記号と小文字のnを単に出力する。改行を出力するには、 print("\n") と書く。

最高に気持ち悪いが、これこそが文字コードの定義において正当なバックスラッシュの表現方法。 「\」はShift_JISで815Fという2バイト文字であり、この値はUnicodeにおいてREVERSE SOLIDUS(U+005C)に対応する。1バイト文字にREVERSE SOLIDUSがなく、重複符号化でないため、2バイトの815Fは全角形にはマッピングされないのが本来の姿。

これで解決できるけど、現実的には難しいですよね、とのこと。

読書メモ

第1章 文字とコンピュータ

  • 文字コード
    • 図形としての細部を抽象化した文字の種類をコンピュータとして扱うための符号
    • 文字とそれに対応するビットの組み合わせを対応付け、16進数に変換した表記を用いる
  • フォント
    • 同一の書体で設計された文字の形のデータ
  • 文字集合
    • 文字を重複なく集めた集合
    • ex. ラテンアルファベット、いろは歌、常用漢字
  • 符号化文字集合
    • 文字集合の各文字に対応するビット組合せを一意に定めたもの=文字コード
    • ex. 英語にはせいぜい100文字程度なので、7ビット(128文字)で表現される=ISO/IEC646国際基準版=ASCII
  • 文字化け
    • ある符号化文字集合により符号化されたバイト列を、別の符号化文字集合に従って解釈すると別の文字になる
  • 内部・外部コード
    • システム内部で処理用に使われる内部コードと、入出力に使われる外部コードは違ってもいい
    • ex. PC用のOSでは内部コードしてUnicodeが多く使われ、入出力の際に変換することでさまざまな文字コードに変換している
  • 制御文字
    • 文字の出力を制御するなどの役割を与えられたコード(↔図形文字)
    • ex. ベル(0x07),水平タブ(0x0B),改行(0x0D),エスケープ(0x1B)
  • 文字コードが複雑になる理由
    • 互換性の保証,漢字が網羅できない・構成要素を合成して表現する文字の扱い,書字方向

第2章 文字コードの変遷

  • 最もシンプルな文字コード ――ASCII
    • 1960年代アメリカの規格として開発
    • American Standard Code for Information Interchangeの略
    • 7ビットの1バイトコードで文字を表す
    • 各国に合わせた変種もある(ISO/IEC 646)
      • 日本版は日本工業規格のJIS X0201(¥,‾記号)
    • 複数の言語を同時に扱えない
  • 文字コードの構造と拡張方法を定める ――ISO/IEC 2022
    • 8ビットコード、2バイトコード
    • ASCIIの符号表が2つ並んでおり、片方(CL/CR領域)をASCIIの文字集合に固定し、もう片方(GL/GR領域)の文字集合を差し替えつつ使う
    • 2バイト文字集合を使えば、94*94=8836文字収録できる
    • 文字集合の切り替えはエスケープシーケンスを使う
      • エスケープシーケンス以外の制御文字でもよい(ex. EUC-JP)
    • 符号化方式:複数の符号化文字集合を組み合わせた運用方式
    • ISO/IEC 2022は符号化方式を定めるための枠組み
    • 「ASCII+自国用の文字集合」といった程度の限定範囲で使われることが多い
  • 2バイト符号化文字集合の実用化 ――JIS X 0208,各種符号化方式
    • JIS X 0208:「JIS漢字」と呼ばれる漢字集合の符号を定める規格(1978年制定)
    • JIS X 0208は2バイトコードを前提としていて1バイトコードを解釈できないため、1バイトコードとの互換性のあるShift_JIS,EUC-JP,ISO-2022-JPといった符号化方式が開発・普及した
  • 1バイト符号化文字集合の広がり ――ISO/IEC 8859,Latin-1
    • 文字数が少ないヨーロッパでは1バイトコードが普及した
    • その結果、1バイトコードが乱立した
  • 国際符号化文字集合の模索と成立 ――Unicode,ISO/IEC 10646
    • 単一の国際的な文字集合を作りたい
    • 同時期に2つの国際文字コードが並立し、最終的にはUnicodeに統合された
      • ISO/IEC 10646:ISO/IEC 2022に基づいて後方互換性を保つ、4バイトコード
      • Unicode:企業が独自に開発した後方互換性のない、2バイトコード
    • 2バイト(65,536文字)では世界中の文字を収められない
      • 10646の4バイト表現だとUnicodeの符号と互換性がない
      • UTF-16:Unicodeの上位2バイトを2つ並べ、拡張領域の1文字を表現する符号化方式
      • UTF-8:ASCIIとバイト単位で互換となるよう変換を施した可変長の符号化方式
    • Unicodeはすでに主要な文字の収録を完了している 世界中の文字を一つの表に収める ISO/IEC 10646とUnicodeの誕生と統合 Unicodeの拡張と各種符号化方式の成立 ――UTF-16,UTF-8 国際符号化文字集合の現状

第3章 代表的な符号化文字集合

  • ASCIIとISO/IEC 646 ――最も基本的な1バイト文字集合
    • 両者は同じ符号化文字集合だが、ASCIIがあくまでもアメリカの規格なのに対し、ISOは国際規格
  • JIS X 0201 ――ラテン文字と片仮名の1バイト文字集合
    • 日本版のISO/IEC 646
    • ラテン文字集合:ASCIIと2文字違うだけ(\と¥,~と‾)
    • 片仮名集合:濁点・半濁点は独立した文字として定義されている
    • 2バイトのJIS X 0208の登場によって必要がなくなったが、Shift_JISの1バイト部分として利用が続いている
  • JIS X 0208 ――日本の最も基本的な2バイト文字集合
    • 日本で使われる漢字・平仮名・片仮名を収録
    • 文字の集合だけでなく、文字に対応するビット組合せも定めている
    • 文字集合の特徴
      • 記号類:≦,∞,仝,〆,...
      • ギリシャ文字:Λ,λ,アクセント記号はなし
      • ラテン文字:発音記号付きはなし
      • 平仮名・片仮名:両者は第2バイトが同じ
      • 漢字:第1水準(日常的によく使われる漢字)は読みの順、第2水準(使用頻度の低い漢字)は部首・画数順
    • 改訂の年代によって78JIS、83JIS、97JISがある
    • 包摂:個別具体的な形状をとって現れる文字を認識する際に行う抽象化
      • 包摂基準:ある要素(文字)を集合に含むか否かの判定基準
    • 外字:足りない文字を文字コード表の空き領域に独自に割り当てたもの
      • 機種依存文字:ベンダが独自に定義した外字(問題は「文字」ではなく「文字の符号」)
  • JIS X 0212 ――補助漢字
    • JIS X 0208に足りない文字を補うための2バイト符号化文字集合
    • 文字集合の特徴
      • 非漢字:アクセント記号付きのラテン文字やギリシャ文字,©,~,丸付き数字はなし
      • 漢字:大修館書店『大漢和辞典』を参照+それ以外の42文字(鯳)を含むが、人名や地名についてはまだ漏れがある
    • ISO/IEC 2022に則っていないShift_JISでは扱えないので、JIS X 0212はあまり使われていない
  • JIS X 0213 ――漢字第3・第4水準への拡張
    • JIS X 0212があまり使われなかったため、改めて作成された「完成版のJIS X 0208」
    • 制定年と改正年をとってJIS2000とJIS2004がある
    • JIS X 0212はJIS 0208と組み合わせて使う前提だったのに対し、JIS X 0213はJIS 0208のスーパーセットであり、置き換えて使う
    • 94*94のコード空間では足りないため、94*94の符号化文字集合を2面もつ
    • 文字集合の特徴
      • JIS X 0208の上位互換として11,233文字を収録
      • 記号類:〽,€,①,⬅,✓,﹅,☖,☀,♠,〼,..
      • 13区の機種依存文字:Ⅰ,㍼,㌔,...
      • ラテン文字:発音記号、アクセント付き含む
      • ASCIIとの互換文字:JIS X 0208はASCIIやJIS X 0201の文字をほぼ含んでいる一対一で対応しないものもあった
      • アイヌ語用片仮名:セ゚,ツ゚,ト゚
      • 鼻濁音:か゚,き゚,く゚,け゚,こ゚
      • 漢字(第3・第4水準):地名、人名、教科書に使われる文字など、世の中に実際の需要がある文字を選定
    • JIS X 0208が定義するすべての符号化方式に対応する9種類の符号化方式が用意されており、スムーズな移行ができる
    • 2009年現在においてあまり普及していないが、Emacs,Python,PostgreSQLなどで実装されている
  • ISO/IEC 8859シリーズ ――欧米で広く使われる1バイト符号化文字集合
    • 第1部から第16部まで、欠番となっている第12部を除き、合計15部のパートがある
    • 各パートが、ASCIIの上位互換となる8ビットの1バイトコードを定義している
    • 現在はISO/IEC 10646の開発に注力のため、新たなパートの開発は行われていない
  • UnicodeとISO/IEC 10646 ――国際符号化文字集合
    • Unicode=ISO/IEC 10646=UCS(Universal Multiple Octet Coded Chracter Set)
    • JIS X 0221としてJISにもなっている
    • 符号の構造
      • ISO/IEC 10646はもともと4バイト符号(=UCS-4)であり、2バイトのサブセットの符号(UCS-2)がある
        • UCS-4:4バイトがそれぞれ群(group)、面(plane)、句(row)、点(cell)に対応し、全部で231の符号位置がある
        • UCS-2:群00の面00に相当する面であり、基本多言語面(BMP,Basic Multilingual Plane)と呼び、元々のUnicodeにあたる
      • UCS-2(=Unicode)ではBMPの文字しか表現できないため、UTF-16が開発された
      • UTF-16だと17の面の符号位置しか表現できないため、群00の面10より先の面は文字を割り当てない
      • 通常用いる文字の多くはBMP、歴史的な文字や記号が面01、BMPに収まらない漢字が面02、特殊用途の符号が面0Eに定められている
    • 結合文字:Unicodeは複数の部品の合成によって1文字を表すことができる(ex. â=U+00E2=(U+0061 U+0302))
  • UnicodeとUTF-8,16,32とUCS-2,4の関係
    • Unicode
      • 整数値で表される符号位置と文字とを対応付ける
      • ex. 「山」の符号位置はU+5C71
    • UTF-8,16,32
      • その整数である符号位置をコンピュータで用いるバイト列で表現するための方式
      • ex. 0x5C71をUTF-16で符号化すると5C 71の2バイト、UTF-8ではE5 B1 B1の3バイト
    • UCS-2
      • 符号化文字集合と呼ぶか文字符号化方式と呼ぶかは言葉の定義次第
      • ex. 「山」という文字に5C71という16ビットが対応する

第4章 代表的な文字符号化方式

  • JIS X 0201の符号化方式
    • 8ビット符号
      • 8ビット符号表のGL領域にラテン文字集合を、GR領域に片仮名集合を呼び出して用いる
      • 8ビットが使用可能な環境で使う
      • Shift_JISのベース
    • 7ビット符号
      • 3種類
        • ラテン文字用:GL領域にラテン文字を呼び出し、切り替えなしに用いる
        • 片仮名用:GL領域に片仮名文字を呼び出し、切り替えなしに用いる
        • ラテン文字・片仮名用:両方をGL領域に呼び出し、制御文字(SHIFT_OUT,SHIFT_IN)で切り替える
      • 7ビットしか使えない環境(電子メールなど)で使う
  • JIS X 0208の符号化方式
    • 漢字用7ビット符号
      • 構造:JIS X 0208をGLに固定して用いる
      • 特徴:すべての文字が2バイト固定で、かつ7ビットで表現される
      • 用途:データの格納や交換
    • EUC-JP(Extended Unix Code)
      • 構造:GLにASCIIを固定、GRにJIS X (0208|0201|0212)を切り替えて用いる
      • 特徴:2バイトコードは必ず第8ビットが立った状態で表現されるため、0x7F以下のバイト値は常にASCIIとして解釈できる
      • 用途:ASCIIの拡張でありISO/IEC 2020とも整合するため、理解しやすくUnix以外でも扱いやすい
    • ISO-2022-JP
      • 構造:GRは利用せず、エスケープシーケンスによってGLを切り替えて用いる
      • 特徴:エスケープシーケンスによる切り替えがあるため、状態を持つ
      • 用途:7ビット環境(電子メールなど)、情報交換用
    • Shift-JIS
      • 構造:JIS X 0201の8ビット符号を元にして、GRの空き領域とCR制御文字領域を使って2バイトコードの第1バイトとして用いる
      • 特徴:句・点番号と第1・2バイトが対応するわけではなく計算が必要なため複雑であり、重複符号化の問題もある
      • 用途:JIS X 0201との互換性が必要な状況に適するが、Shift-JIS自体が普及している
  • Unicodeの符号化方式
    • Unicodeは当初は16ビット固定長だったが、現在は符号化の単位によってUTF-(8|16|32の符号化形式ある
    • UTF-16
      • 構造:BMPの文字は符号単位1つ(16ビット)、BMP以外は符号単位2つの組み合わせ(サロゲートペア)で表す
      • 特徴:8ビットのバイト列として表現する場合に上位から並べる(ビッグエンディアン)か下位から並べる(リトルエンディアン)かという問題が発生するため、どちらを採用しているか示すBOM(Byte Order Mark)をつける
      • 用途:内部処理に適するが、1つの符号位置がもはや16ビット固定長でなくなりつつあり、UTF-32のほうが使いやすい
    • UTF-32
      • 構造:Unicode符号位置をそのまま32ビットで表す(UCS-4と実質同じ)
      • 特徴:すべての符号位置が4バイトで表され、UTF-16と同じくバイト順問題がありBOMをつける
      • 用途:1つの符号位置が固定長になるため、UCS-2やUTF-16との置き換えに適する
    • UTF-8
      • 構造:1つの符号位置の表現に1バイトから4バイト(または6バイト)までの長さで表す
      • 特徴:
        • ASCIIの上位互換とみなせる
        • 1つのUnicode符号位置に対し複数の符号化があり冗長である
        • バイト順問題がなく本来BOMは不要だが、「UTF-8である」ことを示す目的でBOMがつくことがある
      • 用途:元々ASCIIだったデーターフォーマットやプロトコルの拡張

第5章 文字コードの変換と判別

  • 変換のツール
    • iconv:世界の様々な文字コードに対応
    • nkf:日本語環境で古くから利用されている(自動判別機能もあり)
  • 変換の原則
    • コード変換
      • 符号化されたテキストの文字を変えずにコードだけを変換する
      • 異なる文字集合間の変換は対応しない文字がある場合がある
      • ex. JIS X 0208からUnicodeへの変換
    • 文字変換
      • 文字を変換する
      • ex. 大文字から小文字、平仮名から片仮名
  • 変換の処理方法
    • アルゴリズム的な変換
      • ex. JIS X 0208におけるSO-2022-JPからEUC-JPへの変換は、エスケープシーケンスと0x80の足し引き
    • テーブルによる変換
      • ex. JIS X 0208とUnicodeの間の変換は、変換表を引く
  • 文字コードの自動判別
    • 自動判別:バイト列の特徴から文字コードを判別する
    • 判別のツール:nkf,kcc
    • 仕組み:BOM、エスケープシーケンス、バイト列の特徴をみる
      • 自動判別を助ける文字を入れるテクニック:「入口」という文字
        • EUC-JPではC6 FE B8 FD => FE,FDはShift_JISには現れない
        • Shift_JISでは93 FC 8C FB => 93,8CはEUC-JPには現れない
    • Shift_JISとEUC-JPは理論的に完全ではなく、推測でしかない(特に短い文字)
    • 明示したほうがいい

第6章 インターネットと文字コード

  • 電子メールと文字コード
    • 当初はヘッダも本文もASCIIで符号化していた(日本語はISO-2022-JP)
    • バイナリデータはuuencode(ASCIIの図形文字領域に収まるように変換)を使って本文に貼り付けていた
    • MIMEによって様々な文字コードが扱えるようになり、charsetパラメータで各パートの文字コードを指定する
    • テキストの符号化
      • 8ビットコードは符号化して7ビットに収める
      • quoted-principle
        • 方式:第8ビットが0である0x7F以下のコードはそのまま通し、0x80以上は0x7F以下になるように変形する
        • 用途:ときどき0x80以上のコードが現れるとき
      • base64
        • 方式:1バイトに6ビットまでしか含めないように変形し、6ビットずつASCIIの1文字に対応させる
        • 用途:大部分のバイトが0x80のとき(日本語のEUC-JPなど)
    • ヘッダの符号化
      • B符号化:バイト列を一定の規則で変換し、ASCIIの特定の範囲内に収める
      • Q符号化:ASCIIはそのまま、ASCII以外を符号化する(quoted-principleとおなじ)
    • 添付ファイル名の符号化
      • ヘッダのパラメータにASCII以外を使う方法はRFC2231として定義されているが、準拠しないメーラも多い
    • 日本語メールの符号化
      • 過去:ISO-2022-JPを用いて、(base64などを用いず)7bitでそのまま送信
      • 近年:UTF-8を用いて、8bitまたはbase64、quoted-principleで送信
      • まだ前者のほうが無難
  • Webと文字コード
    • HTML
      • 使える文字コード:Unicodeならなんでも
      • 入力できない文字:文字参照、実体参照を使う(ex. &lt; => <) - 文字コードの指定: <head><meta http-equiv:"Content-Type" content="text/html; charset=UTF-8">
    • CSS
      • 使える文字コード:Unicodeならなんでも
      • 文字コードの指定: @charset "UTF-8"
    • XML
      • 使える文字コード:なんでも使えるが、Unicodeを参照するのが妥当
        • 要素名や属性名に漢字や平仮名を使える(ex. <名前>,<住所>)
      • 入力できない文字:文字参照、実体参照を使う
      • 文字コードの指定: <?xml version"1.0" encoding="UTF-8"?>
    • URL
      • 使える文字コード:ASCII
      • 予約記号やASCII以外の文字はURL符号化する(ex. http://example.com/%B6%E2%C2%F4)
    • HTTP
      • 文書内部に文字コードを指定することの問題点
        • 問題点1:複合するファイルの中に復号に必要な文字コードが宣言してある
        • 問題点2:文書内部に宣言する方式はコード変換に弱い
      • HTTPヘッダで文字コードを指定すればよい Content-Type:text/html; charset=utf-8
        • HTMLでmeta要素のhttp-equiv属性に書くと同じ扱いになる(HTTPのほうが優先される)
      • charsetの値を使うにはWebサーバで指定する

第7章 プログラミング言語と文字コード

  • 省略

第8章 はまりやすい落とし穴とその対処

  • トラブル調査の必須工具
    • 16進ダンプツール:od
  • 文字化け
    • 種類
      • ラベルの不備(本体との不一致や文字列の間違い)による文字化
      • 機種依存文字に起因する文字化け
    • 文字化け防止の原則
      • 文字コードを明示する
      • 文字コードに決められた範囲のコード値のみ用いる
  • 改行コード
    • トラブル
      • 1つのファイル中に混在
      • 想定外の改行コードを使用
    • 改行コードの変換:mkf,tr
  • 「全角・半角」問題
    • 区別の始まり
      • 1バイトコード(JIS X 0201,ASCII)へ2バイトコード(JIS X 0208)をShift_JISなどによって追加したことにより、「A」と「A」が生まれた
      • 1980年代の機器は80文字×25行程度の表示能力しかなく、漢字1文字は英数字2文字分の領域を使って描画していたため、1バイト文字を半角、2バイト文字を全角と読んだ
    • 文字コード上は「全角・半角」ではなく「2バイト・1バイト」の違い
    • 「全角・半角」問題への対応
      • 入力文字の検証
      • 重複符号化された文字の同一視
  • 円記号問題
    • 日本の通貨の円記号とバックスラッシュが1バイトコードにおいて入れ替わることがある現象
    • ASCIIとJIS X 0201ラテン文字との差異による問題
    • 対処のための注意点
      • EUC-JPの場合:0x7E以下のバイトとはASCIIなので問題ないかと思いきや、コード変換プログラムによっては勝手に文字変換するから気をつける
      • 文字入力の際:仕様上推奨されていないが、常に2バイトのほうを使う
    • 問題の根本は、コード値が同じ0x5CだからといってJIS X 0201の円記号とASCIIのバックスラッシュを同じものと扱っていること
  • 波ダッシュ問題
    • JIS X 0208の1区33点にある波ダッシュが、UnicodeではU+301になったりU+FF5Eになったりすることにより、文字化けになる問題
    • 対処案
      • Unicodeに変換しない
      • コード変換を揃える
      • Unicode間で変換する

*1:日本語やマルチバイト特有の問題など海外書籍では扱われないため?

*2:日本の漢字(岩波新書)-笹原 宏之