最近、とあるところでユニコードの話題(サロゲートペア)が出ました。ユニコードは昔勉強していたつもりだったのですが、すっかり忘れていたので情報を整理しました。
キーワード
ユニコードを理解するために必要なキーワードです。
ユニコード
符号化文字集合や文字符号化方式を定めた文字コードの業界規格である。
コードポイント
文字集合中の文字をあらわす符号位置。文字を一意に表すことができる。ユニコードコンソーシアムによって決められる。
ユニコードのコードポイントの範囲は、BMP、SMP合わせて、U+10FFFFの範囲に収まる。
Unicodeでは、文字集合中の文字をあらわす符号位置(コードポイント、符号点を参照)に、「Unicodeスカラ値」という非負整数値が割り振られている。Unicodeスカラ値を文章中などに記す場合などは "U+" の後に十六進法でその値を続けることで表す。BMP(Basic Multilingual Plane, 基本多言語面)内の符号位置は U+0000 〜 U+FFFF の4桁(16ビット)で表すことができ、SMP(Supplementary Multilingual Plane, 追加多言語面もしくは補助多言語面)以降は U+10000 〜 U+10FFFF の5桁または6桁(最大21ビット)を必要とする。
文字符号化方式
コードポイントの符号化方式。
- UTF-7
- UTF-8
- UTF-16
- UTF-32
- UTF-9
- UTF-18
文字集合
ユニコードに登録されている(コードポイントを持つ)文字一覧。日本語の場合、JIS X 0201、JIS X 0208、JIS X 0213など。ユニコードのバージョンが上がるタイミングで文字が追加されることもある。
各バージョンごとに追加された日本語は、Unicode - Wikipediaの章「ユニコードのバージョン」にまとまっています。
フォント
フォントとは、グリフとコードポイントを対応づけるデータを含むもの。
文字の骨組みとなる形を字体と呼びます。この字体に一定のスタイルでデザインを施したものを書体と呼びます。また、類似の概念として字形があります。字形はJISの定義によると、「字体を、手書き、印字、画面表示などによって実際に図形として表現したもの」となります。
印刷やコンピュータ上で用いられる書体を、通常、フォントと呼びます。また、個々の字形をグリフ(Glyph)と呼びます。
サロゲートペア
wikipedia(ユニコード)の説明がわかりやすい。
後述の「𠮷」(コードポイント=0x20BB7)の 場合、low surrogate=0xD842、high surrogate=0xDFB7となる。
サロゲートはUnicodeの符号位置の U+10000 〜 U+10FFFF の範囲を16ビットユニットのペア(2つ)で表現する集合で、最初の16ビットユニットを high surrogate、二番目を low surrogate と称する。high surrogates は U+D800 〜 U+DBFF の範囲、low surrogates は U+DC00 〜 U+DFFF の範囲である。
文字(コードポイント)と文字コード(UTF-8/UTF-16)対応例
※) 16進数表記
文字 | コードポイント | UTF-8 | UTF-16 | 名前 |
---|---|---|---|---|
A | 0041 | 0041 | 0041 | Latin Capital letter A |
あ | 3042 | E38182 | 3042 | HIRAGANA LETTER A |
ア(全角) | 30A2 | E382A2 | 30A2 | KATAKANA LETTER A |
ア(半角) | FF71 | EFBDB1 | FF71 | HALFWIDTH KATAKANA LETTER A |
゙(濁点) | FF9E | EFBE9E | FF9E | HALFWIDTH KATAKANA VOICED SOUND MARK |
゚ | FF9F | EFBE9F | FF9F | HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK |
𠮷 | 20BB7 | F0A0AEB7 | D842DFB7 | (※)サロゲートペア |
問題点
JavaのStringクラス
Java(内部文字コードはUTF-16)のStringクラスは、サロゲート文字「𠮷」の長さを2と返す。
System.out.println("𠮷".length);
-> 2
半角カナの濁点文字「ブ」の長さを2と返す。
System.out.println("ブ".length);
-> 2
「ブ」=「フ」(FF8C) +「 ゙」(FF9E)として取り扱われるため。
一方、ブはブ(30D6)として1文字になる。フを格納している領域には、ハングル互換字母の半角形も入っているため濁点付き半角カタカナをいれるスペースがなかったのだろうか?
UTF-8ベースの言語は、サロゲートペア文字の長さを1と返します。
$ iPython
In []: len('𠮷')
Out[]: 1
ただし、「ブ」の長さは2になります。
In [10]: len('ブ')
Out[10]: 2
DB
テーブルのデータ型によって文字コードが変わる。
Oracleの場合には、サロゲートペア文字がVARCHAR2(UTF-8)、1文字として格納されるため、VARCHAR2(UTF-8)にしておくのが無難なようです。
VARCHAR2: DatabaseのCharset依存
NVARCHAR2: Unicodeのみ(Oracleを含む大抵のDBではUTF-16でエンコードする)
NLS_CHARSET=AL32UTF8
NLS_NCHAR_CHARSET=AL16UTF16