このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
HTML には、実体参照と呼ばれる仕組みがある。
& や @ 等がそれだ。
正確には、前者を文字実体参照、後者を数値文字参照と呼ぶ。
乱暴な言い方をすると、HTML のエスケープ構文である。
今までの設計では、日記やタイトルを取得する際には、
正規表現を使って HTML を直接解析していたが、
実体参照に関しては考慮していなかった。
なので、MixiDiaryEntry#getContent メソッドで、
日記の本文を取得した場合は、< 等の実体参照や、
mixi が自動的に挿入した <br> タグが含まれている。
元の日記の本文は、テキストとして入力した文字列なので、
getContent が HTML に依存した結果を返すのはおかしく、
ただのテキストとして取り出せるようにすべきである。
そのためには、mixi が返却した HTML を解析し、
実体参照や改行などを処理しなければならない。
これらの影響を受けるのは、「文字列」である。
日記で言えば、タイトルや本文などが対象となる。
では、その役割を持つメソッドを作成しよう。
以前作った HttpUtil.js の中に定義する。
今回は、JavaScript ならではの方法を使ってみる。
========== part of HttpUtil.js ==========
/**
* HTML の文字実体参照と数値文字参照を展開する。
* @addon
*/
String.prototype.decodeHTML = function () {
// 参照を表す正規表現リテラル
var ref= /&(\w+|#\d+|#x[0-9a-f]+)(?:[;\r]|(?![-.:\w]))/ig,
// 1 つの参照を展開する関数
var func = function (match, name, offset, source) {
if (name.charAt(0) == "#") {
// 数値文字参照
var code;
if (name.charAt(1).toLowerCase() == "x") {
// #x0000
code = parseInt(name.substring(2), 16);
} else {
// #0000
code = parseInt(name.substring(1), 10);
}
if (isNaN(code)) return match;
return String.fromCharCode(code);
}
// 文字実体参照(手抜き)
switch (name) {
case "amp": return "&";
case "gt": return ">";
case "lt": return "<";
case "quot": return '"';
case "nbsp": return "\u00a0";
default: return match;
}
};
return String(this).replace(ref, func);
}
========== end of part of HttpUtil.js ==========
まず、String.prototype.decodeHTML に登録している。
String プロトタイプを書き換えることによって、
あらゆる String オブジェクトから利用できるようになる。
これには、賛否両論あるだろうが、
俺は「Object プロトタイプ」以外なら、
独自にメソッドを追加しても良しと考えている。
デコードするのは文字列である HTML なので、
文字列(型・値)のメソッドとすればしっくりくるのだ。
var html = "~";
var text = html.decodeHTML();
そして、ポイントは、String#replace メソッドだ。
この replace メソッドは非常に強力で、
検索対象として正規表現が使える上に、
置換対象として関数を渡すことができる。
ref は、Perl などではお馴染みの正規表現のリテラルだ。
文字実体参照は、& で始まり識別子が続き、
セミコロンか改行で終わる。
セミコロンや改行がない場合は、
識別子として使えない文字が来た時点で終わる。
数値文字参照は、&# で始まり、10 進表記の数値か、
前に x がつく 16 進表記の数値が続く。
終端に関しては文字実体参照と同じである。
replace メソッドに渡す関数は、
第 1 引数に、一致した文字列全体が渡される。
その後に、括弧でキャプチャした部分文字列が続く。
上記の rex は括弧が 1 つだけなので、1 つだけだ。
そしてその後に一致した位置が渡され、
最後に検索されている文字列全体が渡される。
RegExp#exec の戻り値が展開されたような変な引数だ。
func 関数では、括弧で束縛した参照の名前を、
name 引数として受け取って処理をしている。
#x のものは 16 進数、# のものは 10 進数として
数値に変換し、String#fromCharCode で文字に変換して返す。
それ以外は、文字実体参照として適当に処理する。
HTML には物凄い数の文字実体参照があるので、
全て書くと面倒だ。厳密にやりたい人は追加してな。
さて、関数の戻り値は置換後の文字列として使われる。
なので、不明な参照(や怠慢で処理しない参照)が
関数に渡された場合は、一致部分をそのまま返す。
そうすれば、置換は発生しないことになる。
さて、テストしてみよう。
WScript.Echo( "">d&#x??".decodeHTML() );
これを実行すると、以下のように表示された。
">d&#x??
よし、問題ないようだ。