[JavaScript] サロゲート・ペアに対応した文字列操作関数を書いてみた
- 2008-11-06
- カテゴリ: Client Side
- タグ: JavaScript 安易な発想 Unicode
JavaScriptの文字列型はUTF-16を採用しているから、サロゲートペアを使用した文字が混ざるといろいろと厄介だったりする。一例としては↓のような感じ。
var s = "𪚲"; // U+2A6B2
console.log(s.length); // 2
console.log(s.split("")); // ["", ""]
ということで、サロゲートペアの扱いを少し楽にする関数を書いてみたからとりあえず公開してみる。
/** 文字列中にサロゲートペアを含む場合はtrue */
String.prototype.hasSurrogate = function() {
return (/[\uD800-\uDBFF][\uDC00-\uDFFF]/).test(this);
};
/** 文字列を Unicode 番号の配列に変換する */
String.prototype.toCodePoints = function() {
var i = 0, len = this.length, points = [];
while (i < len) {
var x = this.charCodeAt(i++);
if (0xD800 <= x && x < 0xDC00) {
var y = this.charCodeAt(i++);
points.push(0x10000 + ((x & 0x3FF) << 10) | (y & 0x3FF));
} else {
points.push(x);
}
}
return points;
};
/** String.fromCharCode() を 0x10000 以上の数値に対応させたもの */
String.fromCodePoint = function() {
var i = 0, len = arguments.length, points = [];
while (i < len) {
var x = arguments[i++];
if (x < 0x10000) {
points.push(x);
} else {
x -= 0x10000;
points.push(0xD800 | (x >> 10));
points.push(0xDC00 | (x & 0x3FF));
}
}
return String.fromCharCode.apply(String, points);
};
面倒だったからStringオブジェクトをそのまま拡張してしまった。気に入らない人はthisを適当な変数にして引数として渡してやるように書き換えるとよい。
解説
/** 文字列中にサロゲートペアを含む場合はtrue */
String.prototype.hasSurrogate = function() {
return (/[\uD800-\uDBFF][\uDC00-\uDFFF]/).test(this);
};
見ればわかるとおり、正規表現でサロゲートペアを含むか否かを判断しているだけ。わざわざ関数化するほどのものではないんだけど、サロゲートペアの /[\uD800-\uDBFF][\uDC00-\uDFFF]/ っていうのはすぐ忘れそうだから関数化しておくといいかも。
/** 文字列を Unicode 番号の配列に変換する */
String.prototype.toCodePoints = function() {
var i = 0, len = this.length, points = [];
while (i < len) {
var x = this.charCodeAt(i++);
if (0xD800 <= x && x < 0xDC00) {
var y = this.charCodeAt(i++);
points.push(0x10000 + ((x & 0x3FF) << 10) | (y & 0x3FF));
} else {
points.push(x);
}
}
return points;
};
文字列をコードポイント番号の配列に変換する。基本的にはString.prototype.charCodeAt()各文字に対して適用して配列化しているだけだけど、サロゲートコードを検出したらコードポイント番号に変換している。
ちなみに、ちょっと手抜きをしていてサロゲートペアの二文字目のコードのチェックを省いている。壊れた文字列を渡したら壊れた結果が出てくるかもしれない。本当は、二文字目のコードが 0xDC00-0xDFFF に収まっているかどうかを確認して、収まっていない場合に例外を投げた方がいいかもしれない。
/** String.fromCharCode() を 0x10000 以上の数値に対応させたもの */
String.fromCodePoint = function() {
var i = 0, len = arguments.length, points = [];
while (i < len) {
var x = arguments[i++];
if (x < 0x10000) {
points.push(x);
} else {
x -= 0x10000;
points.push(0xD800 | (x >> 10));
points.push(0xDC00 | (x & 0x3FF));
}
}
return String.fromCharCode.apply(String, points);
};
String.fromCharCode()に0x10000以上の数値を渡すと0x10000以上の桁が無視されてしまう (ECMAScript 3の仕様書に明記されている!) ので、0x10000以上の数値を検出したときにサロゲートペアに分解してString.fromCharCode()に渡す関数。0x10000以上の数値を渡した時の挙動以外はString.fromCharCode()と全く同じ (はず) なので、パフォーマンスを気にしなくていい場合はString.fromCharCode()の代わりに日常的にこっちを使ってもいいかも。
まとめ
最近、ビットをいじるのが楽しいなー
参考文献
トラックバックURL
- http://liosk.blog103.fc2.com/tb.php/162-a2442a5f
1 件のトラックバック
- [JavaScript] サロゲートペアの扱い方をわかりやすくメモっておく
-
先日、JavaScriptでサロゲートペアに対応した文字列関数を書いたりしてみたが、これだけだとサロゲートペアの扱い方を思い出すにはわかりづらい...
- 2008-11-13
- 発信元: 文系大学的IT系の悲哀

