Webアプリケーションにおける文字の扱い
ふと気になってソフトウェア上で扱われる文字について調べみました。
コンピューターはどのように文字を扱うのか
世の中で使われている文字や数値、記号は、それぞれ所定の非負整数値と対応付けられています。この値を所定のスキームに基づいて符号化し、データ列として扱えるようになったものがコンピューター上で扱われます。
Unicode
各文字等がどの非負整数値(コードポイント)と対応付けられるのかについては、Unicodeという標準規格に定められています。Unicodeは世の中にある全ての文字を扱う目的で定められており、対応付けられる値は 0
から 10FFFF
の16進数で表されます。例えば N
という文字は U+004E
と対応付けられています(U+
はUnicodeコードポイントであることを表す接頭辞です)。
合計で約111万の値を扱えるようになっており、これだけの数であればあらゆる文字を格納出来ると考えられています。
文字符号化
世界中のあらゆる文字を効率的に表現し、将来の拡張にも対応できるよう、Unicodeは各文字に固有のコードポイントを割り当て、これを最大21ビットの範囲で表現します。コードポイントを実際にコンピューターが扱える最大4バイトのデータ列に変換することを文字符号化と言います。
Unicodeの符号化には UTF-8
、UTF-16
、UTF-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) という制御文字を使って複数の絵文字を結合して作られたもの(合字)がそれにあたります。詳しくは参考文献を御覧ください。
参考文献
- The Unicode Standard, Version 11.0: https://www.unicode.org/versions/Unicode11.0.0/UnicodeStandard-11.0.pdf
- IT用語辞典: ASCII 【American Standard Code for Information Interchange】 アスキー: https://e-words.jp/w/ASCII.html
- Go Packages: builtin package: https://pkg.go.dev/builtin
- JavaScript Primer: 文字列とUnicode: https://jsprimer.net/basic/string-unicode/
- Java(tm) Platform, Standard Edition 8 API仕様: クラスString: https://docs.oracle.com/javase/jp/8/docs/api/java/lang/String.html
- 絵文字👨🏻🦱は何文字としてカウントする?関連する文字コードの仕様を詳しく調べてみた: https://qiita.com/comware_harase/items/59c60ab1c6e1797f0821