読者です 読者をやめる 読者になる 読者になる

講習会「文字集合と文字エンコーディング」について

なかなか豪快な記事(講習会「文字集合と文字エンコーディング」を開催しました — ディノオープンラボラトリ)を見つけたので、ツッコミを書いてみることにしました。ツッコミどころはかなり多いんですが、まぁ世の中の文字コードがらみの記事なんて大半がこんなものです。

文字コード」という語は「正しい」か

スライドの5ページ目は、「文字コード」という言い方は間違いという趣旨に見えますが、そうでもありません。
というのも、文字コードの世界は難しい世界です。複数のレイヤー、複数の国、複数のベンダーにまたがっているものが簡単になるはずがありません。しかし必須要素であるために、十分な知識を持たないまま、または必要性に駆られて十分な知見が集まる前に実装を行ってしまうこともしばしばあります。このことがさらに「歴史的経緯」としてさらに文字コードを難しくしています。例えばHTTPのcharsetパラメータは、charsetという名前にも関わらずエンコーディングを指定します。
このような難しい世界では、しばしば意図的に物事を曖昧に表現する必要が生じます。件の「文字コード」や「いわゆる半角カナ」という表現は、このような場合きわめて「正しい」言い方になります。
例えばJIS X 0208のことを「エンコーディング」と言ってしまったら明らかに間違いなんですが、「文字コード」ならばOKです。UTF-8のことを「文字集合」と言っても間違いなんですが「文字コード」ならばOKです。というわけで、よくわからないなら下手に細かい言葉を使うよりも大雑把な言い回しをしていただいた方がよろしいでしょう。(そもそも、前述の例のように、しばしば文字集合エンコーディングは溶け合う)

仲間はずれはなんだ

スライドの6ページは仲間はずれを指摘させています。が、のっけからISO-2022-JPの誤記があります。それはさておき、さて、仲間はずれはなんでしょう?

  1. ISO-2022-JP
  2. Shift_JIS
  3. EUC-JP
  4. UTF-8

わたしはISO-2022-JPだと答えます。なぜなら、ISO-2022-JPは状態を持つエンコーディング(stateful encoding)だからです。状態を持つエンコーディングをソフトウェアの内部コードに使うには、なかなか労力が必要なので、結構大きな違いです。
Shift_JISと答えてもよいでしょう。これ以外の3つはUnix方面で生まれていますが、Shift_JISCP/MMS-DOS方面で生まれています。
EUC-JPというのもありかな、EUC-JPは未だに公的な標準規格が存在しません。(EUC-JP-2004ならあるけど)
ISO-2022-JPEUC-JP、Shift_JISUTF-8のように2つに分けることもできます。前者はISO 2022の枠組みに沿っていますが、後者は沿っていません。
ISO-2022-JPShift_JISEUC-JPとUTF-8のようにわけてもいいでしょう。前者はバイト列中のASCIIっぽいバイトが2バイト文字の一部である可能性がありますが、後者はASCIIっぽいバイトが現れたらそれは常にASCIIの文字です。
模範解答はUTF-8で理由は文字集合が違うからなんですが、これは微妙なところです。というのもこれらの文字集合はそれぞれ、IANA Character Setの定義でいくと、実は全部異なります。

ISO-2022-JP
ASCII + JIS X 0201-Roman + JIS X 0208-1978 + JIS X 0208-1984
Shift_JIS
JIS X 0201 + JIS X 0208
EUC-JP
ASCII + JIS X 0201 Katakana + JIS X 0208 + JIS X 0212
UTF-8
Unicode

文字集合

スライドの8ページは文字集合の解説ですが、「文字のカタマリ全体」という表現が謎です。使える文字をリストアップしたものとした方がたぶんいいんじゃないですかね。「文字」とは何かとか、「ある文字とある文字が同じか否かをどう決めるか」というのは実は難しい問題なんですが、ここでは割愛しましょう。
次に、JIS X 0208-1990は、第1水準漢字2965字+第2水準漢字3390字+非漢字524字の規格ですので、「非漢字」が抜けています。
はい、でました「UCS-2」。今更UCS-2に触れる必要はないのでカットするべきです。UCS-2とは何かでも書いたんですが、そもそもUCS-2文字集合でなく、エンコーディングです。Unicode基本多言語面(BMP)をバイト列にするためのエンコーディングです。という具合に誤解が多いので、嘘を書くくらいなら削るべきでしょう。そこらの解説にも嘘が多いので、どうしても書きたいのでしたらISO 10646を見ながら書くようにするべきです。

文字集合の例

スライドの9ページは文字集合の例です。非漢字が抜けているのは前述の通り。UCS-2にも触れる必要がないので、Unicodeに置き換えます。すると文字数が約100万文字になります。
文字集合の違いは扱える文字種の違い」とありますが、違いはそれだけではありません。「文字」とは何かとか、「ある文字とある文字が同じか否かをどう決めるか」が違うことがあります。例えば、JIS規格では「高」と「髙」はしごだかは同じ物として扱われています。(つまり、JIS規格に新規にはしごだかを追加すると非互換変更になる)

文字エンコーディング

スライドの10ページは文字エンコーディングの解説です。UCS-2ISO-2022-JPなど、これまでに述べたことの集大成とも言える内容です。「Shift_JIS」のアンダースコアが空白になっています。

ざっくりの理解

スライドの10ページはざっくりの解説として独創的な表現をなさっています。例えば以下のような言い方にするとか

  1. 文字集合で表示できる文字が決まる
  2. 文字エンコーディングでどんなバイト列になるか決まる

それぞれの解説

スライドの11ページ以降ではそれぞれのエンコーディングの解説をなさっています。
Shift_JISの説明が「携帯端末用HTMLでよく使う」なのが時代を感じますね。Windowsで使いまくってる気もするんですが。正確な言葉遣いを心がけるならば「いわゆる半角カナ」という表現にしておきましょう。また非漢字を忘れずに。「Shift_JIS」の0x00-0x7Fは正確にはASCIIではなく、JIS X 0201 ラテン文字用図形文字集合です。が、まぁCP932のことでしょうので、ASCIIでもよいのでしょう。
UTF-16が「原則2バイト」になっているのは危険です。この手の地雷エンコーディングを用いる時は最悪のケースを想定しておかないと後々大惨事になります。つまり、2バイトまたは4バイトとするべきです。

BOM

19,20ページではBOMの話です。UTF-16BEはエンディアン決め打ちエンコーディングなのでBOMを含みません。UTF-16 (Big-endian)が正解でしょう。ちなみに、viで開いて異常が見つからないのは実はそれがVimだからで、FreeBSDのviなど別のvi実装だとわかります。
BOMがつくのは、UTF-8UTF-16UTF-32UCS-2、UCS-4、つまりUnicodeエンコーディングで、エンコーディング決め打ちでない物は一通りつきえます。(正確にはUCS-2とUCS-4のBOMっぽものはBOMでなく「Mark」)
なお、BOMはMarkであって文字ではありません。同じバイト列になるU+FEFF(ZERO WIDTH NO-BREAK SPACE)というものがありますが、別物です。

nkf

22,23ページはnkfの話なんですが、今時は--icと--ocを教えるべきじゃないですかね。あと、自動判定は過信しないで、原則明示的に指定してください。

まとめについて

UTF-8UCS-2を1〜3バイトにする」これも「UTF-8は1〜4バイトにする」の方がいいでしょう。「たいていの場合は3バイトに収まる」なんてのは不必要(かつ有害)な知識です。

補足などについて

「正確に言うとUTF-8の扱う文字集合はUCS-4」も違います。UTF-8はISO 10646版とUnicode版があり、前者はUCS-4ですが、後者はUnicodeです。「説明が長くなりそう」ならばUCS-2BMPの概念自体を省く方がJIS X 0213などが現れた時のコストが少ないでしょう。

ぶくま

ぶくまにもツッコミを入れてみる。

UTF16はバイトの並びに関する記述があるがUCS2にはない

UTF-16UCS-2ね。で、ISO 10646にはUCS-2にもバイトの並びに関する記述がちゃんとあります。

UCS2とUTF16は、番号と数値が同値でも、前者が「文字の集まり全体」を指し、後者がそういった文字集合上の番号とデータ表現との「変換手法」を指すもの。

UTF-16UCS-2ね。で、UCS-2エンコーディングなので違います。

サロゲートのないUTF-16」と書いても間違いではないと思います

UCS-2だとサロゲートペアの片方のみが現れた場合の処理が変わってくるので、セキュリティ的にはかなり微妙な扱いになってきます。プラクティカルには、UCS-2を横着してUTF-16の実装の流用でごまかすのはありでしょうが、UTF-16の実装をどうせBMPしか使わないからとUCS-2の実装で誤魔化すのはアウトでしょう。結局のところ、UCS-2を初心者向けに解説すること自体が間違いと言えます。

うちのぶくまコメントへの返答

うちのコメントにも返答してみることに。

フォントとの関係が未だ解らず…。

表示装置に文字コードと場合によっては付加情報を与えると、表示装置はフォントセットから目当てのグリフを取ってきて、表示します。

これ以上は実装やどういう見方をするかによるので一概には言えません。

UCSのスキーム上に文字を定義したものだし、UCS-2エンコードといっていいのかな。

たぶん根本から理解が間違ってるんじゃないでしょうか。

UCS-2は、ISO/IEC 10646の定義では

13.1 Two-octet BMP form (UCS-2)

This coded representation form permits the use of characters from the Basic Multilingual Plane with each
character represented by two octets.

となっています。また、Unicodeの用語集では“UCS-2. ISO/IEC 10646 encoding form: Universal Character Set coded in 2 octets, limited to the Basic Multilingual Plane.”と書かれています。

つまり、UCS-2は「文字を定義したもの」ではありません、UnicodeBMP用のバイト表現形式です。文字集合ではありません。

バイト列をテキストストリームとして解釈するための識別子だよねぇ>「文字コード

それは極めて一面的な解釈ですね。HTTPのcharsetパラメータやiconvの引数を想定しているとそのような理解になるのかもしれませんし、Web屋ならばそれで十分かもしれません。
しかし、文字コードはアナログな存在である「文字」をデジタル化して取り扱うことにあるわけで、そこでもっとも重要なのは文字を収集し、同定し、それぞれに番号を振る事です。振った番号(例えばUnicode scalar valueだったり、句点番号だったり)を操って正規化してみたり、バイト列にエンコードして通信してみたりといった恩恵は全てそういった苦労の上に成り立っています。
確かに一面は表しているのですが、文字の収集と同定というもっとも困難な仕事を忘れてしまうのは感心しませんね。(2009-05-20更新)