[JavaScript] サロゲート・ペアに対応した文字列操作関数を書いてみた

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

[JavaScript] サロゲート・ペアに対応した文字列操作関数を書いてみた

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

トラックバック

[JavaScript] サロゲートペアの扱い方をわかりやすくメモっておく
先日、JavaScriptでサロゲートペアに対応した文字列関数を書いたりしてみたが、これだけだとサロゲートペアの扱い方を思い出すにはわかりづらい...
  • 2008-11-13
  • 発信元: 文系大学的IT系の悲哀
Twitter時代の文字の数え方
正確には、「Unicode 3.1時代の文字の数え方」なのでしょうが、Unicode 6.0の時代にそれじゃあなんなので。 はじめに 30過ぎて数も数えられない、なんてことになるとは思っていなかったのですが、文字を数えるのはけっこう難しいです。 140を超えた部分は一切受け付けてく...
  • 2011-06-18
  • 発信元: inquisitor

コメント

コメントの投稿

お名前
コメント
編集キー
 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。