おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

Unicodeについて勉強してみた。

はじめに

以下の記事で文字コードの勉強をしました。

oooomincrypto.hatenadiary.jp

今回はそこからUnicodeについてさらに深堀りしていきたいと思います。

Unicodeとは

「はじめに」でリンクした記事にて文字コード符号化文字集合文字符号化方式に分かれており、Unicode符号化文字集合に分類され、さらにUnicode文字符号化方式として以下の3種類が存在することを解説しました。

Unicode文字符号化方式について

それでは上記3つの文字符号化方式の解説をしていきますが、Wikipediaの解説が分かりやすかったので引用します。

UTF-8は1符号化文字を1〜4符号単位で表す可変幅文字符号化形式で、1符号単位は8ビットである。 UTF-16は1符号化文字を1〜2符号単位で表す可変幅文字符号化形式で、1符号単位は16ビットである。基本多言語面の文字を符号単位一つで、その他の文字をサロゲートペア(代用対)という仕組みを使い符号単位二つで表現する。 UTF-32は1符号化文字を1符号単位で表す固定幅文字符号化形式で、1符号単位は32ビットである。

要するにUTF-8は文字によって8ビットで表せるものもあれば16ビット、24ビットというように1文字のデータサイズが可変ということなんですね。 UTF-16は基本的には16ビット固定ですが一部32ビットで表します。(正確に言うと16ビットで表せない1文字については32ビットで表すというなかなかの作り。これがサロゲートペアという問題を引き起こすことに...。) UTF-32は全ての文字を32ビット固定で表すという作りになっています。

なぜ複数の文字符号化方式があるのか

今でこそ基本的にはUTF-8が利用されているかと思いますが、そもそもなぜこんなに複数の種類があるのでしょうか。 それについてもやはりWikipediaの解説が分かりやすかったので引用します。

1980年代の当初の構想では、Unicodeは16ビット固定長で、216 = 6万5536 個の符号位置に必要な全ての文字を収録する、というもくろみであった。しかし、Unicode 1.0公表後、拡張可能な空き領域2万字分を巡り、各国から文字追加要求が起こった。その内容は中国、日本、台湾、ベトナムシンガポールの追加漢字約1万5千字、古ハングル約5千字、未登録言語の文字などである。このようにしてUnicodeの、16ビットの枠内に全世界の文字を収録するという計画は早々に破綻し、1996年のUnicode 2.0の時点で既に、文字集合の空間を16ビットから広げることが決まった。この時、それまでの16ビットを前提としてすでに設計されていたシステム(たとえばJavaのchar型や、Windows NT・Windows 95API)をなるべくそのままにしたまま、広げられた空間にある符号位置を表現する方法として、サロゲートペアが定義された。

要するに「最初は1文字を16ビットで表しましょう!割り当て可能な文字数は216(65536)個です!」というように決められ、それに合わせてWindows内部の仕組みも1文字16ビット仕様に合わせて作られていました。

しかしながら、アジア圏の文字等を追加したりしているうちに「とてもとても216(65536)個には収まりきらない...」ということが判明してしまい、この計画は早々に破綻してしまったのです。 とはいえ、Windowsなどの内部の仕組みは既に1文字16ビット仕様で作られていたため、仕方ないからそれを尊重したうえで足りない文字については2文字分のデータサイズ(32ビット)で表しましょう...となったのです(これがサロゲートペアであり、以下の問題を引き起こす

Windows内部ではUTF-16が使われているということは...そう、UTF-16では表せない文字は内部では2文字分のビット数を用いているので以下の記事のようにJavaScriptの動作が一部変な感じになってしまうのです(期待値は1だが、実際には2が出力されてしまう)。

人間の見た感じでは1文字に見えても内部では2文字分のビット数を用いているので上記のような結果になるのですね...(;^_^A これはしっかり覚えておく必要がありそうです。

BOMについて

時々「BOM付き」みたいな表記を見ますがBOMとは何なのでしょうか。 これについてはまずエンディアンという言葉を理解する必要があります。

エンディアンについては以下のサイトの説明が分かりやすかったので引用します。

https://wa3.i-3-i.info/word11426.html

エンディアンとは2バイト以上のデータをメモリ上に展開したり、どこかに送る際の「バイト単位で見たときのデータの並び順」のことです。

例えば「語」という文字は「8A 9E」という16進数で表せますが、これを「8A 9E」と表現するのか「9E 8A」と表現するのかでエンディアンの種類が異なります。この場合は前者をビッグエンディアン、後者をトルエンディアンと呼びます。 しかしながら各々の文字を見てビッグエンディアン、リトルエンディアンと判別するのも面倒なので、データの先頭にそれぞれを判別する値を付与しました。それがBOMと呼ばれるものです。

UTF-16 →先頭がFF FEというBOM付きならリトルエンディアン、FE FFというBOM付きならビッグエンディアンとなります。

UTF-32 →先頭がFF FE 00 00というBOM付きならリトルエンディアン、00 00 FE FFというBOM付きならビッグエンディアンとなります。

UTF-8は元々8ビットで1文字を表すため、BOMは必要ありません。ただ、UTF-8であることを判別するためにあえてBOM付きにされることもあります(とはいえ、UTF-8でのBOM付きは非推奨らしい)。

おわりに

以上でUnicodeに関する解説でした。 調べていて分かったのですが、どうやらUTF-7UTF-18というのもあるんですね~。 そのあたりも気が向いたら勉強してみたいものです。