JavaScript Stringでサロゲートペアを扱う
JavaScriptで強力なUnicodeを扱う方法について書きます!(嘘)
先月末に発売されたWEB+DB PRESS Vol.78で「フロントエンドの国際化」の記事を書いたのは前回書いた通り。
記事内で、JSの文字列は基本UTF-16なのでサロゲートペアがうまく扱えないっていう問題は書いたけど、じゃあどうすればいいの?っていうのは載せられなかったので書く。
文字数のカウント
「𠮷(U+20BB7、つちよしだ)」や「𩸽(U+29E3D、ほっけ)」はUTF-16ではサロゲートペアで表現するのでlengthが見た目とズレる。
console.log("𠮷野家で𩸽".length); // 7
これを「5文字」とカウントしたいという話。
正規表現を使う方法
たぶん実装が一番シンプルなのが正規表現でサロゲートペアの個数を数える方法。
function strlen(str) { return str.length - (str.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g)||[]).length; } strlen("𠮷野家で𩸽"); // 5
ループ回してcharCodeAt
でも良いけどこっちの方が分かりやすいし短い。*1
ES6 String Iteratorを利用する方法
ES6のString Iteratorはサロゲートペアを考慮してくれるので、これだけでOK。(Firefox 27以降で実装済)
function strlen(str) { return [c for (c of str)].length; } strlen("𠮷野家で𩸽"); // 5
ES6 Spread Operatorを使うともっと短く書けるのは teramakoさんが書かれている とおり。
function strlen(str) { return [...str].length; }
文字数カウントの闇
サロゲートペアだけじゃなくて、やれ空白文字だ制御文字だ、無効な文字は省こうみたいなことを考え始めると大変なことになる。そういう向きにはTwitterの文字数カウントロジックなんかが参考になると思われる。おすすめはしないけど。
コードポイントの取得/コードポイントからの文字生成
もうひとつ面倒なのは、サロゲートペア文字のコードポイント取得やコードポイントからの文字生成。これはJSのcharCodeAt
やfromCharCode
がサロゲートペアに対応していないため。
"𠮷".charCodeAt(0).toString(16); // "d842" 上位サロゲートの値 "𠮷".charCodeAt(1).toString(16); // "dfb7" 下位サロゲートの値
もちろん計算すれば*2できるんだけどちょっと面倒。
これがES6ではcodePointAt
/fromCodePoint
で解決できる(Firefox 29に実装済)。
"𠮷".codePointAt(0).toString(16); // "20bb7" String.fromCodePoint(0x20bb7); // "𠮷"
これでだいぶ楽になりそう。
ただしcodePointAt
の引数は従来の文字数換算でのインデックスなので注意。つまりこうなる。
"𠮷".codePointAt(0).toString(16); // "20bb7": コードポイント "𠮷".codePointAt(1).toString(16); // "dfb7": 下位サロゲート
この辺はUnicodeの扱いが似ているJavaみたいにisSurrogatePair
, isHighSurrogate
みたいなAPIがもう少し増えてくるとやりやすくなりそうだけど、残念ながらいまのところES6 draftにはないっぽい。
- 作者: WEB+DB PRESS編集部編,栗林健太郎,安詮院康広,山口良平,尾上忠輔,大川高志,坂本寛樹,青木峰郎,増井雄一郎,中島聡,江島健太郎,中島拓,柴田博志,伊藤直也,登尾徳誠,片桐崇,後藤秀宣,佐藤鉄平,近藤宇智朗,長野雅広,奥野幹也,渡邊恵太,A-Listers,家永英治,はまちや2,川添貴生,原田勝信,和島史典,城倉和孝,安達俊雄,Akira,川嶋賢一
- 出版社/メーカー: 技術評論社
- 発売日: 2013/12/21
- メディア: 大型本
- この商品を含むブログ (5件) を見る
ということで、2013年2014年最初の記事でした。今年もどうぞよろしく。