サロゲートペア
2 byte (16 bit)で収まり切らなくなったユニコードを、4 byte (32 bit)で表現する仕組み 問題
Unicodeをメモリ上で持つ方法として、JavaScriptは、UTF-16を採用している
しかし、一部の文字は16 bit (0xFFFF = 65,535)で収まり切らなくなった!
なんと、絵文字は、全部0x10000以上の位置で定義されているため全絵文字は16bitで収まらない!!
code:js
'💩'.length => 2
解決
そこで、2文字分のメモリを使って一文字を表す
0x10000以上の場合は
D800〜DBFFとDC00〜DFFFの組み合わせで表現する
むずかしさ
1文字扱いの文字と、2文字扱いの文字が混在する
でも昔Shift-JISやらEUC-JPに苦労したことがある人は、慣れっこだと思う
UTF-8は1byte〜4byteの可変長で表現するので同じである code:js
'魚'.length => 1
'𩸽'.length => 2
'♡'.length => 1
'💩'.length => 2
実装のヒント
String.protoptype.split, substr, length等の返す値がおかしくなる
splitのかわりにES2015のArray.from(str)で配列に分割すると大丈夫 Scrapboxではこういうのを宣言して使ってる
code:string.js
// lengthのかわり
String.prototype.__defineGetter__('charLength', function () {
return Array.from(this).length
})
// substrのかわり
String.prototype.charSubstr = function (...args) {
if (args1 > 0) args1 += args0 return Array.from(this).slice(...args).join('')
}
正規表現でのマッチング方法
サロゲートペアのマッチング
'💩'.match(/([\uD800-\uDBFF][\uDC00-\uDFFF]|.)/g) => ['💩']
これが
Array.from('💩') => ['💩']と等価になる
なんと、uオプションが使えるようになったらしい
これで、サロゲートペアを意識せず、ユニコードの pointでマッチできる
代表的なサロゲートペアが必要な文字
𩸽(ほっけ)
ユニコードポイント \u29E3D
サロゲートペア \uD867\uDE3D
絵文字すべて
code:js
'𩸽'.length => 2
'💩'.length => 2
おまじない
サロゲートペアの求め方
code:js
function stringFromCodePoint (codeNum) {
var cp = codeNum - 0x10000;
var high = 0xD800 | (cp >> 10);
var low = 0xDC00 | (cp & 0x3FF);
return {high, low}
}
サロゲートペアからJS文字を作成
code:js
return String.fromCharCode(high, low);
ぼやき
rakusai.icon
普通に文字列を32bitの配列で扱えばサロゲートペアなんていらないはずなのになんでこんなややこしいことになってるんだろう?
32bitの配列として扱う方法はUTF-32と呼ばれる
これを採用しているシステムも存在する
32bitなら、2の32乗 = 4,294,967,295 までいけるから当面大丈夫だろう
JavascriptがUTF-16を内部文字列として採用しちゃったからか... これから作るプログラミング言語はみんなUTF-32にするだろうな
Rubyは、内部の文字列もバイト列で持つらしいので、UTF-32も選択可能なのかな?