自前のantennaプログラムが、えらく重たくなっていることは前々から気づいてた。けど、今イチやる気が起きなくて延々放置してたのだが、この度、ようやく物理的にも重い腰を上げてみた。
計ると10分も処理にかかってる。その間、perlはCPUを食い荒らし、筐体内温度も上がってく。今回の修正で処理時間は20秒ほどになった。やれやれである。
HTML::Entities
XML::RSSは1.12から、実体参照の処理にHTML::Entitiesを利用するようになった。内部のデータはencode_entities_numericで処理され、utf8フラグが付いているとか、そういう配慮無しに日本語はすべて数値参照になってしまった。問題のXML::RSS::Private::Output::Baseの当該箇所はこうなっている。
while ($text =~ s/(.*?)(\<\!\[CDATA\[.*?\]\]\>)//s) {
# we use &named; entities here because it's HTML
$encoded_text .= encode_entities($1) . $2;
}
# we use numeric entities here because it's XML
$encoded_text .= encode_entities_numeric($text);
これだけだと分かり難いので、サンプルで。
$ cat a.pl #!/usr/bin/perl use HTML::Entities qw/:DEFAULT encode_entities_numeric/; use encoding utf8; my $in = '<テストだよ>'; print $in, "\n"; print encode_entities($in), "\n"; print encode_entities($in, '<>&"'), "\n"; print encode_entities_numeric($in), "\n"; print encode_entities_numeric($in, '<>&"'), "\n"; $ perl a.pl <テストだよ> <テストだよ> <テストだよ> <テストだよ> <テストだよ>
こちとら、2行目の「<テストだよ>」のようにして欲しいわけだが、みな、3行目のようになり、何が書いてあるのかデコードしないとわからなくなってしまった。まぁ、RSSなんだからプラウザで読めればいいという話ではあるのだが。
ま、とにもかくも、HTML::Entitiesの使い方は日本では不評であった。これに対抗するにはどうすりゃいいか、なんとかencode部分を差し替えられんかと、あれこれ考えていた。XML::RSSをバージョンアップせず、1.10を使い続けるというのも一手。もしくは改造しちゃう。最初の頃はそうしてた。あるときから面倒になって、とりあえず、encodeされた状態を出力させ、後でdecodeするというのに切り替えた。XML::RSSは、FreeBSDのportsで導入してるので、バージョンアップしないでいるのも面倒だから。
「XML::RSS 字化け」でググると、encode_output => 0をしてるページが沢山ヒットしてしまう。それはどうよ。XMLパーサーにかけるとエラーになるRSSが生成されっぞ。まぁ,他人事ではあるが。
encode_cb
さて、今回の件を調べていて、CPU喰っているのは実体参照のencodeをしている部分であることが分かった。なんかできんかと、XML::RSSのPodを読み返していて、new Methodのところに、encode_cbなるパラメータが増えていることを知った。まさに、encode部分の差し替え機能である。エンコード部分を自前で用意してreferenceで渡すだけでいい。こんな感じになる。
sub my_encode {
my ($self, $text) = @_;
#return "" unless defined $text;
if (!defined($text)) {
confess "\$text is undefined in XML::RSS::_encode(). We don't know how "
. "to handle it!";
}
return $text if (!$self->_main->_encode_output);
my $encoded_text = '';
if ($text =~ /\[CDATA\[/) {
while ($text =~ s/(.*?)(\<\!\[CDATA\[.*?\]\]\>)//s) {
# we use &named; entities here because it's HTML
$encoded_text .= encode_entities($1, '<>&"') . $2;
}
}
# we use numeric entities here because it's XML
$encoded_text .= encode_entities($text, '<>&"');
return $encoded_text;
}
...
my $rss = new XML::RSS(encode_cb => \&my_encode);
XML::RSS::Private::Output::Baseの_default_encodeから、二箇所を変更している。
encode_entities_numericは使わない。unsafe_charsを明示指定して、余計なエンコードを抑制
これでXML::RSS 1.10相当に戻る。
utf8フラグ
日本語文字列が数値参照になってしまうのは、これで防げたが、CPU喰いまくりなのは直らない。何が? これだ。
while ($text =~ s/(.*?)(\<\!\[CDATA\[.*?\]\]\>)//s) { }
CDATAセクション内を実体参照しないための処置なのだが、このパターンマッチをutf8フラグの立ったデータに行うと時間がかかる。マッチだけで10秒近く。なにやらperl 5.8.9からのような気がする。とりあえず、上のように、CDATAが無ければマッチに行かないように変更して逃げてるんだけど、いっそperlを5.10にしてしまった方がいいのかも。
0 件のコメント:
コメントを投稿