概要
- サザエさんの過去の手 一覧に、サザエさんのじゃんけんの手がデータベース化されている
- XMLHTTPRequest#responseText は、HTTPヘッダで文字コードが指定されていないと、Shift-JISなどの文字がうまく読めないらしい
- ADODB.Stream を使って、文字コードの符号化、復号化、相互変換が可能
経緯
サザエさんの過去の手 一覧というページで、サザエさんの放送で予告の最後に出される、じゃんけんの手がデータベース化されていることを知り、とりあえず集計してみることにした。
なんだか文字化けしてしまう
ところが、MSXML2.HTTP を使って サザエさんの過去の手 一覧をダウンロードし、じゃんけんの情報を抽出しようとしたところ、うまく正規表現がマッチングできない。html を出力してみると文字化けしており、XMLHTTPRequest#responseText で、うまく復号化できていないことが原因のようだ。
調べてみると、サザエさんの過去の手 一覧のHTTPヘッダは、文字コードが指定されておらず、そういう場合、XMLHTTPRequest#responseText は、UTF-8 で復号化をするようだ。ところが例のページの実際の文字コードはShift-JIS、ということで、今回の問題になったようだ。ちなみに、ローカルのファイルを呼んでも同様の模様。
この問題自体は、Ajax 周りでは既に広く認識されているらしく、Ajax でデータをやりとりする際にはUTF-8で、という認識があるようだ。
デフォルトがUTF-8というのは、やっぱりXMLのデフォルトの文字コードがUTF-8だからだろうね。
対策
Firefox 3 からは、XMLHTTPRequest にsetRequestHeader メソッドが追加されており、未確認だけど、それで文字コードを設定することによってうまく復号化できるという情報もあった。
しかし JScript には、そんなメソッドはないので、あれこれ調べた結果、ADODB.Stream という COM を使えば、かなり自由に文字コードの変換ができるということがわかった。
この Stream オブジェクトは、カーソル位置を持ったシーケンシャルなデータ構造で、さまざまな方法・形式でデータの読み書きができるので、データ変換に便利に使えそうな気がする。
最初は、XMLHTTPRequest#responseText を Stream に読み込ませようとしてエラーが発生していたが、XMLHTTPRequest#responseBody だとうまく読みこめた。このあたり、動的型付けというか、戻り値の型が Variant ですなんてリファレンスに書かれていて非常に困る。
集計結果
| じゃんけんの手 | 頻度 | 確率 |
|---|---|---|
| 休み | 29 | 0.03 |
| パー | 302 | 0.31 |
| グー | 298 | 0.31 |
| チョキ | 322 | 0.33 |
| チョキ(観月ありさ) | 1 | 0.00 |
| チョキ(カツオ) | 1 | 0.00 |
| Total | 953 | 1.00 |
ということで、まあ当然のように、ほぼ 1/3 ずつになりました。
じゃんけんの手はどうやって決めているのかなあ。下手な決め方していたら、頻度や出現条件や、どこかが偏ってくるだろうから、さいころとかで決めているのかも。
コードは以下。WSH/JScript で。
// 情報源のURL
var url = "http://www.asahi-net.or.jp/~TK7M-ARI/sazae_ichiran.html";
// XMLHTTPでHTMLをダウンロード
var http = WScript.CreateObject("MSXML2.XMLHTTP");
http.open("GET", url, false);
http.send();
// ADODB.Streamを使って、ダウンロードした内容をShift-JISで復号化
var stream = WScript.CreateObject("ADODB.Stream");
stream.Type = 1; // StreamTypeEnum.adTypeBinary = 1
stream.Open();
stream.Write(http.responseBody);
stream.Position = 0;
stream.Type = 2; // StreamTypeEnum.adTypeText = 2
stream.charset = "shift_jis";
var text = stream.ReadText(-1); // StreamReadEnum.adReadAll = -1
stream.Close();
// 正規表現でデータを探す
var reg = /第(\d+)回\s+(\d\d)\.(\d\d)\.(\d\d)\s+(.*?)\s*</g;
var records = [];
var sazae = {}; // 集計用
var match;
while((match = reg.exec(text)) != null) {
var record = {
num: match[1],
date: new Date((91<=match[2]?1900:2000)+match[2], match[3]-1, match[4]),
sazae: match[5]
};
if(!sazae[record.sazae]) {
sazae[record.sazae] = 0;
}
sazae[record.sazae]++;
records.push(record);
}
// 集計結果を出力
var out = "";
for(var c in sazae) {
out += c + "\t" + sazae[c] + "\t" + sazae[c]/records.length + "\n";
}
out += "Total\t" + records.length
WScript.Echo(out);
参考文献
- ぁゃιぃ(*゚ー゚)NEWS <http://semimaru.s47.xrea.com/>
- サザエさんの過去の手 一覧 <http://www.asahi-net.or.jp/~TK7M-ARI/sazae_ichiran.html>
- XMLHttpRequestのresponseTextで扱える文字コードについて – Backstage of theater.js <http://d.hatena.ne.jp/susie-t/20060808>
- Windows Script Wiki – FAQ <http://winscript.s41.xrea.com/wiki/index.php?%5B%5BFAQ%5D%5D>
- XMLHttpRequest Object <http://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx>
- Stream オブジェクト <http://msdn.microsoft.com/ja-jp/library/cc364272.aspx>
- サザエさん (テレビアニメ) ? Wikipedia <http://ja.wikipedia.org/wiki/%E3%82%B5%E3%82%B6%E3%82%A8%E3%81%95%E3%82%93_(%E3%83%86%E3%83%AC%E3%83%93%E3%82%A2%E3%83%8B%E3%83%A1)>
- XMLHttpRequest – MDC <https://developer.mozilla.org/en/XMLHttpRequest>