Skip to main content

Webアプリケーションにおける文字の扱い

💻 Tech

ふと気になってソフトウェア上で扱われる文字について調べみました。

コンピューターはどのように文字を扱うのか

世の中で使われている文字や数値、記号は、それぞれ所定の非負整数値と対応付けられています。この値を所定のスキームに基づいて符号化し、データ列として扱えるようになったものがコンピューター上で扱われます。

Unicode

各文字等がどの非負整数値(コードポイント)と対応付けられるのかについては、Unicodeという標準規格に定められています。Unicodeは世の中にある全ての文字を扱う目的で定められており、対応付けられる値は 0 から 10FFFF の16進数で表されます。例えば N という文字は U+004E と対応付けられています(U+ はUnicodeコードポイントであることを表す接頭辞です)。

合計で約111万の値を扱えるようになっており、これだけの数であればあらゆる文字を格納出来ると考えられています。

文字符号化

世界中のあらゆる文字を効率的に表現し、将来の拡張にも対応できるよう、Unicodeは各文字に固有のコードポイントを割り当て、これを最大21ビットの範囲で表現します。コードポイントを実際にコンピューターが扱える最大4バイトのデータ列に変換することを文字符号化と言います。

Unicodeの符号化には UTF-8UTF-16UTF-32 の3つの形式があります。それぞれにある数字は符号単位を表していて、例えば UTF-8 の符号単位は8ビット(1バイト)となります。ASCII文字のような1バイトで表せる文字は1符号単位で扱われ、そうでないものは最大で4符号単位で扱われるので、UTF-8で扱われる文字のサイズは1〜4バイトのいずれかになります。UTF-16は2バイトまたは4バイトで文字を表現します。UTF-32は常に4バイトで文字を表現します。それぞれの形式には長所もあれば短所もあるので、用途や対象とするデータ、システムの要件に応じて適切に使い分けることが求められます。

各言語の比較

Webアプリケーション開発に使われるいくつかの言語で、文字がどのように扱われるのかを見ていきます。尚、各言語は筆者にとって親しみ深いものを選んでいます。

Go

文字表現には rune という型が使われており、これは int32 の値のエイリアスです。4バイトの情報を扱えるため、Unicodeコードポイントである 0 から 0x10FFFF の範囲の値をそのまま保持することができます。

一方で文字列を表現する string 型は、ASCII文字や多くの文字を扱う際にメモリ効率が良くなる、UTF-8でエンコードされたバイトのスライス(可変長配列)を内部的に保持しています。string 値を一文字ずつ走査して rune 型の文字として扱う際は、バイト列から rune 値への変換が必要となります。

Java

文字表現に使われる char 型は2バイトの情報を扱うことができ、UTF-16の符号単位を表しています。2バイトを超える情報量の文字を表す際には、サロゲートペアという仕組みを使って符号単位2つで表現します。文字列型(String)は内部的に char の配列として実装されていますので、文字列の走査で char の各要素を見ていくと、サロゲートペアで表現される文字、例えば多くの絵文字などは適切に表現されないといったことが起こるので注意が必要です。

TypeScript (JavaScript)

明示的な文字型は無く、単一の文字も含め文字は string 型の値として扱われます。内部的には2バイトの符号単位の配列として扱われているため、Javaと同じく、文字によってはその表現のためにサロゲートペアが必要となり、個々の文字へアクセスする際にはそれに対しての考慮が必要となります。

尚、TypeScriptは基本的にJavaScriptの型システムを拡張したものですので、文字表現の基本的な部分はJavaScriptと同じです。

合字の絵文字

ここまで、4バイト以下で表現される文字について話をしてきましたが、絵文字についてはこれに当てはまらないものもあります。Zero Width Joiner (ZWJ) という制御文字を使って複数の絵文字を結合して作られたもの(合字)がそれにあたります。詳しくは参考文献を御覧ください。

参考文献

感想はこちらにどうぞ 👉️ @curryisdrink