Chienomi

WindowsがUTF-8(BOMなし) を標準に

ソフトウェア

長かった…

Windows 10 Insider Preview Build 18298(19H1) でメモ帳がUTF-8(BOMなし)に対応し、これをデフォルトとした。

わかる人はこれだけで十分だろうから、わからない人にむけて解説しよう。

日本語WindowsはシステムロケールとしてMSCP932という文字エンコーディングを採用していた。 この文字エンコーディングはJIS X0201及びJIS X0208文字集合を表現するShift JIS文字エンコーディングをベースに、NECとIBMがShift JISがX0208のテーブル外を指しているポイントに独自に文字を定義した。

これはJIS X0208を拡張しているわけではなく、Shift JISがJIS X0208に対して直接マップされたものであることからShift JISがJIS X0208のテーブルを指しているよりも後側、2バイトで表現できる範囲は未定義であり、ここを昔からの慣例通り「外字」として好き勝手に使用していた。 同様にNECとIBMが勝手に定義したものなのだが、広く使われればあたかも規格であるかのように扱うことができる。 Microsoftはそれを規格にしたCP932という文字集合と文字エンコーディングを採用したわけである。 事実、CP932とShift JIS(JIS X0208)には一部同一ポイントで異なる文字がある。

だが、文字集合や文字エンコーディングは合意ありきのものであり、「独自のもの」というのはそのOSに閉じ込められた文化になってしまう。 基本的にはMicrosoftはこれを「Shift JIS」であると呼んだ。それどころか、「ANSIである」とすら言った。

ANSIというのは国国家規格協会のことであり、ANSI Character EndingといったらANSIが決めたASCII(US-ASCII)のことを指すのだが、Windowsで「ANSI Code Page」といったらMSCPと呼ばれる、Microsoft独自の文字集合兼文字エンコーディングを指している。これは、「システムローカル」のような意味で、気軽に勝手に名乗られたANSIはキレていいと思う。

当時、WindowsはMSCP932(Shift JISとは微妙に互換性がない)にCRLF改行文字、MacはMacでNECの独自のShift JIS拡張を取り込んでさらに拡張したMacJapaneseなる文字集合兼文字エンコーディングを採用し、CR改行文字、Unix系はEUC-JP文字エンコーディングにLF改行と世は大戦争である。

ちなみに、日本語WindowsにおけるCP932は

  • Windows-31J
  • MS932
  • CP932
  • MS漢字コード
  • MSCP932
  • OEMコードページ932
  • Shift JIS

という呼び方があり、勘違いも含めてそれぞれ意味は違う。地獄だ。 ところが、「Windows-31Jは互換性問題解消のために生まれた」ところがまた地獄だ。

ここで話はさらに複雑になる。Windows NTはまだ登場間もない文字集合Unicodeに対するUTF-16LEをシステムエンコーディングとして採用し、新しいファイルシステムであるNTFSの文字表現もFATと違いシステムローカルのMSCPではなく、UTF-16LEを使用するようになった。 このときはまだサロゲートペアが導入される前のお話である。当時は、65536文字でこの世に存在する全ての文字が表現できると(少なくともアメリカ人には)信じられていたし、それが幻想だと気づいていなかったのだ。だから、65536文字を表現できるUTF-16こそが最も美しい文字エンコーディングだと(少なくともアメリカ人には)信じられていた。

だが、それがまた悲劇を生む。この世界の文字は65536文字では収まらなかったし、サロゲートペアは日常的に使うものだったし、UTF-16に十分なメリットはなかった。

現在主流となっているUTF-8が拒まれた理由は可変長文字エンコーディングであったことだ。 現在UTF-8は1文字を表す長さは1バイトから6バイトまである。 これに対してUTF-16は単位が2バイトであり、「2バイトが1文字」として扱える。これは、「ファイルを途中へシークしても文字の区切りはどこか予め知ることができる」というメリットがあり、また、文字の長さを知る必要がないということもある。

だが、現実はそうはいかなかった。サロゲートペアによってUTF-16も事実上可変長文字エンコーディングになってしまったのだ。 頑なにUTF-16のメリットが損なわれたことを認めない人は今でもいるけれど、今世の中は「UTF-16にしてもどうせ可変長なのだし、それだったらUTF-8がいい」という意見が大勢をしめている。

UTF-8はバイト列であり、「1バイトずつ読んで処理する」ということになっている。これは、エンディアンが関係ないというポイントがある。 エンディアンは「ビットの並び」ではなく、「単位中のバイトの並び」であることに注意してほしい。 例えば2バイト単位であれば、2バイトのうち「上位ビットは先にくるのか後に来るのか」という問題が発生するのだ。 なんでこんな問題が発生するのかというのは、様々なケースを示さないとならないので、ちょっとここではやめておく。要は「どの時点でコンピュータの事情にあわせてひっくり返すか」のタイミングが違う。

ところが、可変長の場合はそうはならない。長さがわからないので複数バイトを一気読みすること自体できないのだ。 なので、先頭1バイトを読んでこれを第1バイトとし、ここで完結していなかったら次の1バイトを読んで第2バイトとする、というような振舞いになる。だから、解釈する方法はひとつしかない。

世の中はUTF-8が標準になってもWindowsとしてはそうやすやすとUTF-16LEをやめますというわけにはいかなかったし、頑なにUTF-16LEを守り続けてきた。Windows 10になって以降は、さすがにMicrosoftとしても危機感を持ち始めてUTF-8に対応しつつある。

だが、Microsoftはまた過ちを重ねてしまった。 ひとつは、UTF-16LEを「Unicode」と呼び、UTF-8を「UTF-8」と呼んでしまっていること。そして、MicrosoftのUTF-8にはBOMがついていることだ。

BOMはバイト列の並びを表すためにつける印である。 これがどのようなバイト列で並んでいるのかということに基づいてLEなのかBEなのかを判断できる。

だが、先程も言ったように、UTF-8は「並べる順番」というものが存在しない。にもかかわらずBOMをつけるのは無意味であるといっていい。 しかし、Windowsの場合、これを「UTF-8の識別方法」として使っている。「先頭のバイト並びがこうだったらUTF-8」という判定方法をとっているのだ。 公式にはUTF-8としてはBOMは「容認するけど非推奨」である。

にもかかわらずWindowsは常にUTF-8にBOMをつけてきたし、BOMなしUTF-8という選択肢を与えてこなかった。 やむなくBOMなしUTF-8を認める場合でも「UTF-8N」なる名称をつけてこちらが特殊なものであるかのようにみせかけてきた。 BOMなしUTF-8はWindowsでは文字化けフラグだが、どちらかといえばBOMつきUTF-8のほうがトラブルの元である。

これに加えてBOMありUTF-8の問題点として、UTF-8は1バイトで表現できる範囲は0x7FまでASCIIと同一であり、ASCIIのみで表現されたUTF-8文字列はASCIIとして扱えるというメリットがあるのだが、BOMがつくことでこのメリットは消滅する。

Windowsが拡張されたShift JISを採用してきたことも、システムにUTF-16LEを採用したことも、歴史的経緯を考えれば妥当と言えるのだが、UTF-8にBOMをつけたことだけは「邪悪な行い」でしかなかったのだ。 そして、そんなことをするのは基本的にWindowsだけであり、誰も歓迎しなかったのである。

だから「せっかく標準のUTF-8にしたのに、変なものを仕込んで標準を破壊し面倒な処理を強要するWindows滅ぶべし」だったのだが、ようやくMicrosoftもみんなと協力する気になった、というわけだ。

本当に長かった。多分、Officeもそのうち対応してくれることだろう。