自前の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 件のコメント:
コメントを投稿