ぺんぎんらぼ

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です。たまに絵を描きます。

お笑いとマンガ好きなしょぼしょぼWeb系エンジニアの日記です

いくつになっても「データと文字」を理解できない自分へ④ - Unicodeコードの悲劇

今回は文字コードの最終回「Unicode」について説明します。

前回、紹介した「JISコード」や「シフトJISコード」で、ひらがなや漢字など、日本語全般の文字を扱うことができるモードの登場により、PCで日本語を容易に扱えるようになりました。
これは、コンピュータの処理性能の向上が大きく関係しています。扱える文字種が多くなるということは、データ量が増えることにほかなりません。コンピュータの処理性能の向上により、増えたデータ量を処理できるようになったのです。

文字コード乱立時代

日本では、コンピュータ上でひらがなや漢字を使いたいという需要があったのと同じように、諸外国でも、同じような需要は当然のようにありました。
日本と同じように、漢字文化の中国や、複数の文字を組み合わせて文字を表すハングル文字など、文字種が多い国でも、コンピュータで母国語を扱えるよう、日本と同じように独自の文字コードを定義しています。

日本国内にしても、PCでは「シフトJISコード」、UNIX系OSでは「EUCコード」、ホスト系OSでは「EBCDICコード」と、複数の文字コードが存在しました。

これら、文字コードが乱立したことで次のような問題が顕著になってきます

国ごとに文字コードが違う

複数の国で使用されるようなグローバルシステムでは、複数の国の文字を使って画面を表示したいところですが、1つのシステムで複数の国の文字コードを扱うことが難しいのです。

OSによって文字コードが違う

日本国内だけでも、OSによって文字コードが違うため、複数のOSからなるシステムの場合、相互に文字コードを変換する必要があります。
例えば、データをホストで保存しているような場合、それをPC上で表示するときは、PC上のクライアントアプリケーションで、ホストの文字コードEBCDICコード」をPCの文字コードシフトJISコード」に変換する必要があります。

Unicodeの登場

いくつもの文字コードを処理するアプリケーションは、文字コードの変換に実装コストや処理コストがかかります。
また、テキストファイルを開くときに文字コードが違うと、文字化けを起こしてしまいます。

扱う文字コードが増えることは不利益しか生まないことから、文字コードを統一しようという動きにつながりました。

ISOの文字コード規格委員会は、世界のすべての文字を扱える文字コードの規格化を始めます。
実は、この動きはかなり昔で、1984年です。Windows95よりはるかに前で、キャラクタベースのMS-DOS全盛期です。
そして、1991年に最初のUnicodeの仕様が公開され、バージョンアップを重ねて、扱える文字が増えていくことになります。

Unicodeとは

Unicodeは、すべての国の文字を扱える文字コードです。

シフトJISコードは、1バイトで1文字を表す半角文字+2バイトで1文字を表す全角文字で成り立っています。
それに対し、Unicodeは、すべての文字を2バイトで表します。2バイトなので65536の文字を表すことができます。

CJK統合漢字とは

ここで、すべての国の文字を2バイトなので65536文字で表現できるか?と疑問に思うかもしれません。
各国のコードで定義されている文字種は、日本で6000種以上、中国で7000種以上、韓国で8000種以上、台湾で13000種以上です。これだけで、半分以上の領域を使ってしまいます。

これらの国の文字で多くを占めるのは、「漢字」です。そこで、Unicodeでは同じ形の文字は1つの文字に統合することで、必要となるコードの数を減らすことにしました。
つまり、日本語の「約」も中国語の「約」も同じコードにしてしまう、ということです。

これらの文字の集まりは、Chinese(中国語)、Japanese(日本語)、Korean(朝鮮語)の頭文字をとって、CJK統合漢字と言います。

Unicodeの普及

Unicodeはバージョンアップを重ねることで、扱える文字が増えていきます。
それに伴い、UnicodeをサポートするOSや言語も増えていきます。WindowsUnixMacOSなどは、文字の処理にUnicodeが使用されていますし、Javaも文字表現はUnicodeが使用されています。そして、Javaは、Unicodeと、Unicode以外の文字を相互に変換する機能も備えているので、Unicode以外のOSとのデータ連携が容易となっています。

Unicodeの問題

すべての文字を収録した素晴らしい文字コードであるUnicodeですが、問題もあります。

文字形の問題

CJK統合漢字で説明したとおり、同じ形の文字は1つのコードに統合されています。
同じ文字なんだから、統合しても問題ない。本当でしょうか。

次の文字を見てください。

f:id:penguinlabo:20210501001647p:plain

上段が中国のフォントで表示したもの、下段が日本のフォントで表示したものです。 文字の形に違和感を感じます。中国のUnicodeフォントで日本語を表示すると、上段のように一部の文字が中国で一般的な形になってしまうのです。

ASCIIコードとの互換性の問題

これまで、いくつかの文字コードの説明をしましたが、それらの文字コードはASCIIコードとの互換性を保ってきました。互換性を捨てることは、過去の資産を切り捨てることになるからです。
しかし、UnicodeはASCIIコードとの互換性を切り捨てています。

ASCIIコードはすべての文字を1バイト(正確には7bit)で表しますが、Unicodeではすべての文字を2バイトで表します。
互換性を完全に捨てているわけではなく、「00」+ASCIIコードの2バイトで既存のASCII文字を表現します。
Unicodeでは、すべてのASCII文字に「00」が付与されるので、既存のテキストエディタでは正しく表示できず、結果、互換性がないことになります。

また、ASCIIコードだけで事足りる、たとえば英語圏のテキストファイルの場合、Unicodeにすると、すべての文字が2バイトになり、単純にデータ量が2倍になってしまいます。

そこで、UnicodeはASCIIコードと互換性のあるUTF-8を策定することになります。
UTF-8は、ASCIIコード部分は1バイトで、それ以外の例えばCJK統合漢字などは3バイトで表現することにしました。
文字を表す最小ビット数が8ビット(1バイト)であることから、UTF-8命名されてます。それに対し、文字を表す最小ビット数が16ビット(2バイト)のUnicodeは、UTF-16とも呼ばれます。

サロゲートペア文字

Unicodeは、全世界の文字を2バイトで表現する、これが始まりでした。
表現できる文字種は65536文字です。足りなくならないよう、CJK統合漢字のように、同じ形の文字を統合することで、文字種が不用意に増えないようにもしました。

しかし、バージョンアップに従って、文字を追加していったことにより、いよいよ2バイトでは不足してきそうになります。
そこで、サロゲートペアという規格が追加されます。
これは、一部の文字を倍の4バイトで表すことで、さらに多くの文字種を扱えるようにするものです。

この、サロゲートペアが、Unicode最大の悲劇だと思います。

既存のシステムはUnicodeを使うことで、1文字は2バイトで表現できることが保証されていました。
例えば、5文字まで入力できる項目の場合、10バイト以内であることをチェックすればよいのですが、サロゲートペア文字を入力されると、3文字で12バイトとなり、入力エラーになってしまいます。

Javaサロゲートペアの規格追加以前の言語なので、既存のAPIでは正しくサロゲートペア文字を扱えません。
以下のコードを実行してみましょう。

System.out.println("𠮷".length());

「𠮷」は「土」+「口」のサロゲートペア文字です。このコードの実行結果は「2」と表示されてしまいます。「𠮷」は2文字として扱われてしまう、といことです。

サロゲートペアという後付け仕様により、既存のAPIでは対応できず、結局、開発者がサロゲートペア文字を意識したコードを書くことが必要になりました。

Unicodeに物申したい!

ここからは個人的な意見です。

Unicodeの問題点をいくつかあげましたが、マツキはUnicode肯定派です。
すべての国の文字を1つのコードで表現するという、途方もないことを現実のものにしてくれました。

しかし、サロゲートペアだけは避けてほしかった。後付けする仕様としては、既存システムへの影響が大きすぎます。

コードが足りなくなったから、やむを得ず、というのが理由なんですが、本当に足りないんでしょうか?

Unicodeはバージョンアップとともに文字を追加しています。次のように「絵文字」も追加されてます。

f:id:penguinlabo:20210501010307p:plain

この「絵文字」は結構な種類が追加されているうえ、サロゲートペアではない2バイトで表現できるエリアにマッピングされている文字もあります。

これらの文字は、サロゲートペアという仕様を追加してまで必要だったのか、大いに疑問です。

絵文字を取り込まなくても、将来的にコードが不足しそうだから、サロゲートペアを追加したのかもしれませんが、だとしたら、初めの「2バイトですべての文字を表現する」という規格自体に見積もりの甘さを感じます。

最後に

このUnicodeの話を持って、文字コードの話しは終了です。

欠点はありますが、Unicodeは素晴らしい規格です。そして、そのUnicodeと既存の文字コードを相互変換できるJavaも素晴らしい言語です。

エンジニアとして、システムづくりにかかわっていると、文字コードに対する取り組みから解放されることはありませんが、UnicodeJavaのおかげで、多くのハードルがなくなる、もしくは極めて低くなったことは事実です。

文字コード乱立時代の先人の苦労を考えると、いい時代に生まれたな、と痛感します。