
はてなキーワード:UTF-16とは
𩸽(ほっけ)のUnicode符号位置はU+29E3D。これをUTF-16で表すとする。
U+10000以上の符号位置の文字をUTF-16で表す場合、サロゲートペアによって表現される。
まず、Unicode符号位置を表す「U+n」のnに対して、0x10000を減算する。
𩸽はU+29E3Dだから、0x10000を減算すると、nは0x19E3Dとなる。
(なお、Unicode符号位置が0x10000未満である場合は、それは16ビットであり(なぜなら0x10000未満であるとは、最大でも0xFFFFだから)、2バイトで表現される。これはBMPの範疇であり、サロゲートペア表現(BMP外の文字表現)の出番はない。)
(また、0x10000以下の符号位置のうち、Unicode符号位置U+D800~U+DFFFはサロゲートペア用に確保された符号位置領域であり、この領域内の一符号位置に対応する文字は無い。)
$echo "obase=2; ibase=16; 19E3D" |bc11001111000111101↓(不足した桁をゼロで埋める)00011001111000111101
となる。
この20けた(00011001111000111101)のうち、
①上位10桁(0001100111)に対して0xD800(1101100000000000)を足す。これを上位サロゲートと呼ぶ。
1101100000000000 0001100111↓1101100001100111
②下位10桁(1000111101)に対して0xDC00(1101110000000000)を足す。これを下位サロゲートと呼ぶ。
11011100000000001000111101↓1101111000111101
③上位サロゲートと下位サロゲートの組み合わせ(1101100001100111 1101111000111101)が、UTF-16サロゲートペア表現そのものである。
$echo "obase=16; ibase=2; 11011000011001111101111000111101" |bcD867DE3D
$unicode 𩸽UTF-16BE: d867de3d(※"BE"とはbig-endianの略であり、「この16進表現は左から上位バイトとして読みますよ」という意味)
Unicodeにおいて、本来、文字は16bit、つまり65535文字で十分表現できるはずだった。
しかし中国の古代漢字などの文字も収録しようとすると、とても16bit程度では表現できないことが分かった。
そこで、UTF-16という符号化方式においては、サロゲートペアという工夫を使うことで、10万以上の文字を扱えるように仕様を整えた。
正確にいうと、2byte(16bit)では65536文字が表現できる。
ところで、0xD800~0xDFFF(2048の符号位置)はサロゲートペア用に確保されているため、特定の文字の符号位置としては利用できない。
その一方、サロゲートペアによって20bit分(1048576文字分)の符号位置を確保できたため、UTF-16では、
$echo $((65536-2048+1048576))1112064
https://qiita.com/yumetodo/items/54e1a8230dbf513ea85b]
https://b.hatena.ne.jp/entry/s/qiita.com/yumetodo/items/54e1a8230dbf513ea85b]
https://togetter.com/li/1301253]
https://naruse.hateblo.jp/entry/2018/12/24/013446]
文字コードを多少かじった人間としては、また人類が文字コードで混乱している。と思っていて議論が深まるのかなと思ったりします。
ただ、この話、見ててもやもやする所が一つありまして、UTF-8の1コードポイント=uint8_t=unsignedcharでええんかいな。という点です。
文字コードを少しでも知っている人はUTF-8は1つのコードポイントを可変長のバイト列で表します。
よく言われるようにASCIIは1バイト、大体のCJKV文字は3バイト以上で表します((久々にWikipediaのUTF-8見たら、UTF-8にサロゲートペアってあるんだねー。罪深いわOrale〜))。最大6バイトで1つのコードポイントを表します。
つまりですね、char16_tとかchar32_tとかがUTF-16やUTF-32にマッピングされるのは分かるんですよ。サロゲートペアは脇に置いておいて、コードポイントを表すのにはこの型(っつーか、データ長)を使うよってのが分かるので。
サロゲートペアを考えたときのUTF16も同じ考え方になるんですけど、UTF-8みたいな可変長のバイト長を取るエンコード方式は、結局、1「文字」を表す型(データ長)が定まらないんですよ。
char8_tをunsignedcharの子クラスにしたとしてもそれって、UTF-8にとっては「1文字を表す型」ではないんですよ。「1文字を表すバイト列の単位の1つ」でしかないんですよ。(サロゲートペアを考慮したときのchar16_tも同様)。
意味論で言っちゃえばUTF-32に対してchar8_tを使っても意味は同じになるんですよ。UTF-32って8ビット×4で構成されるだけなんで。
なので、UTF-8で表される1文字を型で使いたかったらuint64_tの子クラス(本当は最大6バイトなので48でいいんだけど)にしなきゃダメなんじゃねぇの?もしくは最少8ビットで48ビットを保証する型。とC++界隈ではない自分は思うわけです。
INSERT INTOTBLSELECTTBL.COL FROM OPENROWSET(BULK 'filepath', SINGLE_NCLOB)ASTBL(COL)
他の方法もいろいろあるだろう。ただ、文字コードに関して言及すると、SQL SERVER の内部データはUCS-2(UTF-16のサブセット)なので、合わせておいたほうが無難。UTF-16以外で取り込もうとすると、日本語付近で "XML 文字が無効です" などとエラーになる場合が多い(もちろん原因は何かあるのだろうが、調べてもさっぱり私には見当がつかない。時間の浪費はいやずら)。
'filepath'は、SQL SERVERのインスタンスが動いているマシンにとってのファイルパスね。'E:\???\???.xml'や'\\filesvr01\shared\data\???.xml'とか。
SELECT COL.query('/ELEM/NODE') FROMTBL など、あとはいろいろクエリーしてみてくれ。
(感謝)
PHPユーザー会の中の人とたまたま話したんだけど、アプリケーションのPHP5.2系からPHP5.3系への移行が滞っているようだ。
「業務でPHP5.3使ってますよー。」って言ったらむしろ驚かれた。どういうこった?
いま移行せずに、PHP5.3ってどうなるのよ?その先にあるPHP6系ってどうなるのよ?不安しかでてこない。