Unicodeは、文字やその符号化方式を定義する規格です。
具体的には、文字集合と文字符号化方式の二つに分けられます。文字集合は、文字とそれに対応するコードポイントの対応表です。一方、文字符号化方式はUTF-8、UTF-16、UTF-32など、文字をバイト列に変換する方法を定義しています。これにより、符号化されたデータ列からコードポイントを計算し、対応表から文字を見つけることができます。
Unicodeは、U+10FFFFまでのコードポイントを定義しています。かつて、Unicodeは16ビット幅の固定長で65536個の文字を扱う規格でしたが、現在でもその影響が残っています。例えば、Microsoft Windows環境では、wchar_t のデータ幅は16ビットであり、OSが提供するWin32APIの -W 系のAPIでは、wchar_tを使用しています。
さて、気付くと最近のUnicodeのバージョンは 15.1 まで進んでおり、2024年9月には Unicode 16.0がリリースされる見込みです。そろそろ自分の知識をアップデートする必要性を感じてきました。本記事では、私が重要と考えるポイントをまとめています。同じように知識をアップデートしたい方にとって参考になれば幸いです(将来的に自分が読み返すための外部ストレージとしての狙いもあります)。
前提条件
この記事では以下の基礎知識や条件を仮定しています。
Windows の環境を想定
普段メインで使用しているのがWindowsということもあり、この環境においてどのような状況であるのかに興味があります。そのため、LinuxやmacOS、AndroidやiOSなどはここでの範囲外としています。
UTF-16LEを想定
Windowsの環境という条件に関連して、UTF-16 LEを対象としています。Windowsの内部ではUnicode 16bit幅の名残からか、こちらの符号化方式で動作しているようです。wchar_tに入れる文字列が UTF-16ですし、その点でも合致しています。
なお、UTF-8やUTF-32であれば、符号化をほどくとUnicodeで定義されるコードポイントがすぐにわかるので、UTF-16のような複雑なことが発生せず簡単・シンプルです。
Unicodeにおける最低限の知識
冒頭に述べた符号化方式と文字集合の話を理解していることが必要です。あとはサロゲートペアやNFC/NFDに関するものは耳にはしているくらいは必要です。正規化に関する話はここではしませんが、Unicode の最新世代へ知識のアップデートにはこの話も必要になるでしょう。
Win32APIやC/C++を基本
余計な変換が挟まることを避けたいので、Win32APIとC++の組み合わせでコードを作成します。C#やPython, Java など各言語のランタイムが入るものはここでは扱いません。おそらくそれぞれの言語VMやランタイムでUnicode対応バージョンなど組み合わせがあるものと思います。
私が素の状態から把握をなるべくしたいと感じるところも影響しています。Win32APIはWindowsを使っている上で避けられませんし。C#も悪くないのですが、.NETのランタイムの部分でさらに振り回されるのもツライので、ここでは避けておきます
Unicodeバージョンの節目
私が感じた節目の情報です。
- Unicode 2.0
- サロゲートペアの導入が決まり、U+10FFFFまでのコードポイント領域に広がりました
- ~ Unicode 3.2
- JIS X 0213 正式対応の状態となるようです
- Unicode 5.1
- 異字体セレクタが導入、漢字に対して適用が開始
- Unicode 6.0
- 日本の携帯電話の絵文字が追加される
- ※このあたりから絵文字がUnicodeの文字集合に積極的に入り始めたように私は思います。
なお、Windows10 登場初期の時点で Unicode 5.11 の対応状態だったようで、現在の Windows11 最新バージョンでは既に Unicode 15で、Emoji 15.0も使えるようですね。進化が早い。
異字体セレクタ
異字体セレクタ(VS: Variation Selector)は、直前の文字に作用する文字です。漢字専用版をさらにIVSと呼ぶようです。具体的な例でよくあがるのが渡”邊”ですが、他にも探してみると、”尊”も該当するようです。ベースとなる U+5C0A のコードポイントに対して、異字体セレクタを追加することで、字形が変わるバリエーションが提供されます。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-1-1024x594.png)
こちらは、 https://747.codeberg.page/vsselector/ で調査した結果を示しています。たとえば、U+E0101 の異字体セレクタを追加することでベースの漢字字形とは別のものになっています。
この異字体セレクタもコードポイントを見るとわかるように、表現にはサロゲートペアが必要になるものです。つまり異字体セレクタだけで4バイトを使用し、ベースのコードポイント分も含めると「尊󠄁」の1文字だけでUTF16換算で 6バイトになっています。
現在の絵文字までの道のり
絵文字が単体でUnicodeのコードポイントで割り当てられていることもありますが、現在の絵文字は予想以上に複雑なことになっています。絵文字のカテゴリにおいて、どのような仕組みになっているかを調べてみました。
異字体セレクタによる変更
絵文字に対しても異字体セレクタを使用して、何かしらの作用が発生します。例えば色つき絵文字とか。
シーケンス絵文字
コードポイント1つで絵文字を示すのではなく、複数のコードポイントを使って結合し、それを1文字とするタイプの絵文字のことを、シーケンス絵文字と呼ぶようです。このシーケンス絵文字は、結合後の文字についてUnicodeのコードポイントを持っておらず、さらには絵文字自体も登録されていない(就労されてない)という妙な状態になっているようです。そして、この”シーケンス”ですが、さらにいくつかの分類があるようです。
モディファイヤーシーケンス
人の肌の色を変更可能にするという話題でよく知られているパターンです。
キーキャップシーケンス
2️⃣ な絵文字を実現するものです。これはコードポイントの表現にすると、 U+0032 U+fe0f U+20e3 となります。キーキャップシーケンスによる修飾で、キーキャップのような見た目になります。ここのブログのテキストでもこのコードポイントで貼っていますので、使用しているフォントや環境によって様々な見え方になっていることでしょう
旗シーケンス
国旗のようなものを示す絵文字で、旗専用のアルファベットRegional Indicator (RI)を使って表現します。 JP の組み合わせで、日本の国旗絵文字が出現するようです。
ZWJ シーケンス
これが一番今の絵文字に重要なシーケンスと私は思います。名前が示すのは、Zero Width Joiner で、ゼロ幅結合子です。絵文字でこのシーケンスを用いて、意味的に結合して、複雑で様々な絵文字への対応をしてしまおうという思想となっているようです。
ここまでの全ての要素は同時に使えるので、異字体セレクタ+ZWJシーケンスというのがあり得ます。そしてこれらの表現では従来の 0000~FFFFのコードポイント領域外のものになっているため、サロゲートペアによってUTF-16上で表現されます。たった1文字の絵文字なのに、予想外にバイト数を消費している!となっているのに驚くことがあります。
サンプルを見てみよう
例として、ファミリー絵文字などではこのようになっています。
👩👩👧👦← これはファミリー絵文字です。1文字で表示されていますか?
コードポイントで表現すると、U+1F469 U+200D U+1F469 U+200D U+1F466 U+200D U+1F466 となります。では、これを UTF-16で表現すると・・・
![](https://www.technicalife.net/wp-content/uploads/2024/07/emoji-binary.png)
サロゲートペアによる表現も加わって、見た目1文字ですが22バイトも使っている1文字の状況です。
そして、この絵文字ですが、ファイル名にも使えてしまうところに驚きもあります。エクスプローラーでは白黒文字で表示され、Windows Terminal ではカラーリングされた状態で表示されました。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-3.png)
サロゲートペアの定番、ホッケと共に絵文字を並べてみました。漢字は読めますが、絵文字は白黒になるとなかなか苦しいですね。
普段の開発ツール上での見た目
Visual Studio Code では絵文字について、うまく表示できることが多いようです。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-4.png)
Visual Studio 2022のエディタ上だとこんな感じに。いくつか使用するフォントを変えてみても変化はなかったです。これをみると、ZWJによる合成は他のサイトで説明があったように、同じ見た目を保証しないというのを実感します。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-5.png)
このコンソール出力では、絵文字は全く出力できませんでした。
今度は WPF (C#) で文字を入れてみたところ、こんな感じです。テキストエディタ上では、正確に表示できていないものの、ウィンドウ側では表示が一応できているようです。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-6.png)
C/C++から絵文字を扱うとどうなるか
テキストエディタ上での見た目は確認できたので、ここではC/C++のコードを書いた場合では、Windowsアプリケーション上でどう表示されるのかを確認します。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-7.png)
では、Windows の -A 系列のAPIで UTF-8 を受け付けるように変更した状態ではどうかを見てみます。ここでは以下のサイトで語られているようにマニフェストファイルを準備して、プロジェクトへ登録するものとします。
![](https://www.technicalife.net/wp-content/uploads/cocoon-resources/blog-card-cache/0f3f188b7aaecb7058359243658ac92e.png)
プログラムを全て-A系列のAPIを使うように書き換えた上で、UTF-8文字列として、先ほど同様の絵文字をウィンドウタイトルに含むようにしてみました。結果は以下の通りで、ウィンドウタイトルに絵文字、ZWJを含んだ複雑なものまで出せました。相変わらずテキストエディタ側は不十分ですが。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-8.png)
なお、ウォッチウィンドウでは絵文字を含んだ状態でも表示できるようです。意外でした。
![](https://www.technicalife.net/wp-content/uploads/2024/07/image-9.png)
参考
以下のサイトによって勉強させていただきました。感謝申し上げます。本記事では自分に必要なポイントのみに絞ったこともあり、以下のサイトらでじっくり理解するのがオススメです。
![](https://www.technicalife.net/wp-content/uploads/cocoon-resources/blog-card-cache/57308fdb7a4cfb2874484bf74729d4c4.png)
![](https://www.technicalife.net/wp-content/uploads/cocoon-resources/blog-card-cache/3a9357364883d382238fd4aafa7cd8a8.jpeg)
コメント