先日CPANに活気がない等と
書いてしまいましたが、ほぼ1年ぶりにXML::LibXMLが1.70にバージョンアップしていました。で、早速いれたんですが、なんか無茶苦茶に速くなっていません?インテルマックだと速度差がわかりづらいのでクソ遅いPowerMac G5で試してみますか。
書いてしまいましたが、ほぼ1年ぶりにXML::LibXMLが1.70にバージョンアップしていました。で、早速いれたんですが、なんか無茶苦茶に速くなっていません?インテルマックだと速度差がわかりづらいのでクソ遅いPowerMac G5で試してみますか。
教えてほしいなんて、そんなこと自分で考えな!と普段だったら言うところですが、かわいい研修生にグリーンの瞳で見つめられながら言われては絶対に断れないですよね![]()
(しかしこの子は美人だ...)
ということで久々のスクリプトネタです。
喜んでしかたがないのでどのスクリプトでも通用しそうな方法をPerl様を使って教えました。これけっこう今までも訊かれることが多かったので、色々なやり方がある中で初心者の方にもわかりやすい方法だけを少しだけ書いておきます。まあ実際にはこのような形で使われることはめったにないでしょうが。
前置きとして検索する単語群と検索の対象になる文はどちらも配列に入っているものとすることにします。例では検索する単語群の配列を@tangogun、検索の対象になる文が行ごとに入った配列を@bunとし、全ての検索語にマッチした行だけをプリントすることにします。JavaScriptでも他のスクリプト言語でも似たようなことをすれば(番外編を除いて)動くはずです。
その1 検索する単語群の入った配列の要素数とマッチした回数をチェックし、二つの値が等しい場合にプリントする方法
コマンドラインでしたらcatで検索対象の文を読み込んで(別にcatを使う必要は全くないですがパイプライン処理だと想定して)Perlのオプションで-neを指定すれば下記の例のようにそのまま使えます。例では検索単語群は"Finland"、"restaurant"、"Kamome"ということにしておきます。
その2 検索する単語がマッチすれば的(まと)という変数に当たりマーク与え次の検索語へ進み、そうでなければハズレマークを与えてlastでループから抜け出し、最終的に的(まと)が当たりマークだったらプリントする方法
こちらもコマンドラインでやるにはその1と同様にcatで検索対象の文を読み込んでPerlのオプションで-neを指定すれば簡単にできます。
その3(番外編)検索単語群を「(?=.*Finland)(?=.*restaurant)(?=.*Kamome)」という感じに「(?=.*〜)」を使って連結して検索にかけるとあ〜ら不思議、上記2例と同じ結果が得られます。これは非常に有名ですがPerlでやると正直遅いです。ただSnow Leopardの入ったインテルマックだと速度差は気づきませんけどね。これを使うとPerl様でも全く歯が立たないくらい激速なのはSafariの(WebKitの)JavaScriptです。昔のFirefoxでは(1.Xの頃ね)動きませんでしたが今のバージョンではわかりません。例では検索単語群を連結したものを入れた変数$renketsuを使いました。
コマンドラインではクオート問題を回避するためにq//などを使ってやると下記のようにやると良いでしょう。
さっ、かわい子ちゃんと一緒にお茶してこよ![]()
Pythonを除いて64ビットのものになっていました。で、Perlさまでもとうとう1ギガバイト(GiBではなくGBです)のテキストファイルを扱えるようになりましたよん。できたからって役に立つわけでもないですけどね。
リファレンスを読みながらXSHを少しだけ使ってみました。で、感想ですが、主にPerlと連携で使うならこれけっこう使えます。XSHは名前の通りシェルなので使いこなせるまでにけっこう覚えることがたくさんありますが、XMLの処理に特化しているぶんかなり強力です。特にノードの置換処理などにはもってこいのような感じです。簡単に下記のようなXPathで拾い物をして表示するだけのコマンドラインを書いてみましたが、さすがlibxmlを使うだけあって速い!
foreach my $i in { glob('/Users/misabazooka/Desktop/oshigoto/*.xml') } { my $file := open $i; print (//music[finnish[@shop="iTunes"]]); }
XMLだけではなく色々とやりたい人にはコマンドラインからPerlでモジュールを使ってやった方がXSHを覚えて使いこなすよりは敷居が低いかな。(ご参考までにこちらもどうぞ)
あったんですねえ。何気なくCPANを漁っていたら見つけました。コマンドラインで使うんだったらこちらの方がPerlのlibxmlモジュール使うよりかは便利かも。でも古いっす。これに限らずCPAN自体が最近活気がないような。XPath 2.0のモジュールを誰か出してくれないですかね。libxmlの方では対応しないみたいだし悲しい...
今日久々に自分のブログを見たら検索フレーズランキングで「コマンドライン xpath」が1位になっているではありませんか。実は以前に載っけたヤツは時間がなかったこともあって/usr/local/binにぶち込んであるスクリプトをコマンドライン用に動くよう速攻手直ししたものだったので間抜けなところもありました。で、直そう直そうと思いつつも今日までサボっていましたが、被害が広がるのを恐れて今直しました![]()
前回のとは違ってcatを使いました。単純にXML_DOCフォルダにあるXMLファイルからタグがdivでクラスがfinlandのものを拾ってタグを消して出力するだけです。
for i in /Users/misabazooka/Desktop/XML_DOC/*.xml; { cat $i | perl -MXML::LibXML -MXML::XPath::XMLParser -0777e '$hoge=<>;$parser=XML::LibXML->new;$xmlp=$parser->parse_string($hoge);$sagashimono="//div[\@class=\"finland\"]";$nodeset=$xmlp->findnodes($sagashimono);foreach $node($nodeset->get_nodelist){$node=XML::XPath::XMLParser::as_string($node);$node=~s/<.*?>//g;print "$node\n\n";}'; }
太字のところを交換すれば使えますが、XPathをもっといじりたい方はこちらを参照してください。
モジュールがインストールされていないと動かないのは同じです。ご参考までにどうぞ。
追記 閑話ですが、実は仕事用のコンピュータが全てマルチコアもしくはマルチプロセッサになったので、マシンのプロセッサパワーをスクリプトで100パーセント使おうと思ってPerlのthreadsを使って実験してみました。200以上のファイルの処理をそれぞれ別スレッドを立てて処理するスクリプトとthreadsを使わずに繰り返し文で同数のファイルを処理するスクリプトを書いての単純な速度比較です。確かにthreadsを使ったスクリプトではマルチコアでプロセッサパワーが全開になりましたが、トホホなことに一つのコアしか使わない繰り返し文のスクリプトの方が処理速度は速かったです。まあ、threadsはそういう風に使うものではないと言われてしまえばそれまでですが...なんかXgridでの失敗を思い出してしまいました...
コーヒータイムでの暇つぶしに以前から興味のあった言語内比較も取り入れてやってみました。文字数524, 288, 000で(「A」を524, 288, 000文字含む)500メガバイトきっかりのファイルを使用しました。単純に「A」を「O」に置換するだけです。バージョンはどちらもLeopard標準でPerlが5.8.8、Rubyが1.8.6です。Perlで使用したのはs/A/O/g、tr/A/O/、 tr/A/O/sで、Rubyで使用したのはgsub(/A/, "O")、tr("A", "O")、tr_s("A", "O")です。以下がテスト結果です。どちらも3回計測してベストタイムをとってありますが、1秒以下の違いしかありませんでした。
1. s/A/O/g vs. gsub(/A/, "O")
Perl 1分52秒
Ruby 5分10秒
2. tr/A/O/ vs. tr("A", "O")
Perl 12秒
Ruby 12秒
3. tr/A/O/s vs. tr_s("A", "O")
Perl 4秒
Ruby 4秒
ということで、1のテスト以外は互角でした。やっぱりPerlは速いですねえ。ここまで差が出るとは思いませんでした。言語内比較でtrは速いですが、実際にはこのように単純な置換はまずないでしょう。使えるところに使えば速度アップに繋がるというところでしょうか。
(というよりかはXPath関連ですが)にバグらしきものを発見してしまった。具体的には//AAA[BBB[CCC[@name='HOGE']]]と書いてnameがHOGEのCCCを含むBBBを更に含むAAAを抽出したかったのですがこのままではダメで、//BBB[CCC[@name='HOGE']]/ancestor-or-self::AAAと書いたらうまくいきました。もしかしてわたしのXPathがだめなだけ?もっと勉強せねば...
を使おうとすると色々と難しいですねえ。XMLHttpRequesの部分は問題ないですが、インターフェースを担当するスクリプトはIE用に一から書き直しですねえ。SafariとFirefoxとOperaはほぼ同じスクリプトで動くのに...時間がかかってもいいからということでとうとう手をつけましたが、いくつも作る余裕はないので7以前は切り捨てることにしました。先は長そう...
をBBCが開発していると言うニュースをITmediaで読みました。Perlは最近ニュースが少ないのでこういうの読むとまだまだ捨てられていないんだなあと思いますね。
またまた友人からの質問です。文字列をテキストから検索するみたいに、XML文書をコマンドラインから検索する方法を訊かれたので書いてみます。これはわたしも友人から教わったものです。教わったものにかなりいい加減に手を加えてやっていたものをそのままアップしているので、ベストとは程遠いかもしれません。自分の環境では取り敢えず動いて速いので使っています。少なくともIBMのdeveloperWorksにあるヒント: コマンド・ラインXML処理のXML::XPathを使ったXPath検索よりはずっと速いです。モジュールはXML::LibXMLとXML::XPath::XMLParserを使います。下記の例だと、デスクトップにあるXML_filesというフォルダーの全てのXML文書から出身がフィンランドの人が(= 出身という属性がフィンランドの人)4人以上含まれるグループを検索しています(”//group[count(hito[\@shusshin=\"finland\"]) > 4]”)。最後にタグを消してから出力しています。
for i in Desktop/XML_files/*; do echo -n $i | perl -MXML::LibXML -MXML::XPath::XMLParser -ne '$parser=XML::LibXML->new;$parsed_file=$parser->parse_file($_);$sagashimono="//group[count(hito[\@shusshin=\"finland\"]) > 4]";$nodeset=$parsed_file->findnodes($sagashimono);foreach $node($nodeset->get_nodelist){$node=XML::XPath::XMLParser::as_string($node);print "$node\n\n";}' | perl -pe 's/<.*?>//g'; done
太字の部分が肝心の検索の部分で、ここをいじれば色々と検索できます。イチイチ書くと大変なので、フォーマットを作ってとっておくと良いでしょう。もっと速くて簡単な方法があったら教えてください (^^
追記 XPathで何ができるかはこちらのページに詳しく載っています。
追記2 XPathで意味もなくcontains()関数を使っていたので、短く「属性〜が〜」(@〜="〜")というものに書き換えました。本当に自分で使っている検索では意味があったんですが、この例では意味がありませんでした。
追記3 検索に必要なモジュールは自分でインストールしないとダメです。CPANへGo!
題にある四つにJavaも加えようとさきほど現実逃避も兼ねてネットでJavaの勉強を1時間ほどしてみました。で、ファイルを読んで置換して書き出す単純なものができたのですが、これが無茶苦茶遅い! 原因はわたしが書いたコードにあると思われますが、やはり1時間の詰め込みではいかんともしがたいですなあ。現状で結果をアップしたらJavaマスターに殺されそうなので止めておきます。ただ置換を1行ずつ処理するにしても、今までのスクリプト言語があっさり1分以内に処理を済ませているのに対して、1分30秒以上というのはわたしが書いたコードのせいだけではないと思いますけど、実際はどうでしょう。もしかしてJavaのテキスト処理はもともと遅いのかな? 正規表現がPerl様並みに充実しているので期待していたんですけどね。
Pythonもこけました。Perlもあれこれやってみたのですが、結局はファイルを読み込んでからすぐにメモリがアウトになります。ということでわたしの環境では1ギガバイトのファイルを扱えるのはRubyだけです。まあ1ギガバイトのテキストファイルなんて扱うこともないでしょうけど、RubyにできることがPerlにできないのがしゃくにさわって(笑)
ん〜。先程もう1度1ギガバイトのファイルで試してみたのですけど、Rubyのスクリプトは時間をかけましたが置換はしっかり終えました。一方Perl の方はこけてしまいました。undef $/で一気読みしてたのですけど、これがまずかったのか、それともs///sgがこけているのか調べなくては。今映画を録画中なのでPythonも含めて実験は数時間後に。
makoさん、恐れ入りました!!! Mac proを使っていると知った時にただ者ではないと感じていましたが、Perlguruな方がネット上でお知り合いになった方の中にいるなんてびっくりしました。しかもフィンランドにいらっしゃる! こういうのは心臓に良くありませんねえ(笑)。
で、いきなりですが弟子入りさせてください!!! 後ほど入門手続きにブログの方に伺います!!!!!
Pythonも加えてその2に突入です(笑)
Pythonのバージョンは2.5で使用したのはre.sub()です。
まとめ その1のテスト
Python 5秒
その1のテストが物足りなかったのでファイルの大きさを5倍(513,5メガバイト、置換回数 5,164,200回)に増やしてテストしてみました。結果は
Perl 18秒
Ruby 25秒
PHP メモリがアウトで計測不可能
Python 30秒
とこんな感じです。Perlは痴漢の王者というところですね。はい、Perlさまの実力を疑うようなことはもう二度としません。
ところでRuby も書きやすかったですが、Pythonもなかなか書きやすいですね。時間があったらまた挑戦します。
あまりいい加減な比較をしていても全く参考にならないのでできる限り条件を揃えてもう一度文字列の置換の速度を再テストしてみました。今回は102,7メガバイトの丸ごと読み込んだテキストファイルで繰り返し文は全く使わずに1,032,840回の置換をする作業をさせてみました。使ったのはPerlは5.8.6、Rubyは1.8.2、PHPは5.2.0 (cli)です。置換に使ったのは、Perlがs///gs、Rubyがgsub()、PHPがpreg_replace()です。結果は
Perl 3秒
Ruby 5秒
PHP 6秒
でした。こういうことやらせたらPerlはやっぱり速かった。疑ったわたしがバカでした(笑) 懺悔します!実は1ギガバイトのテキストファイルで実験しようと思っていたのですが、繰り返し文を使わないとスクリプトが途中でダウンしたので止めました。さっ、後はPythonかな(笑)
0.4秒程Perl の方が速かったです。条件はRubyの時と同じです。Perlも今日は5回中3回程1秒切りました。Rubyはコンスタントに1秒切ります。PHPは何度やっても1秒切ることはありませんでした。
と書いたのですが、PHPのスクリプトにミスがあって(古いのを間違って使ってました)、それを直したら、Perl と同じ速さになりました。全てのスクリプトを見直して、最後のまとめ編で結果をお知らせします。
こういう題名を使っているとその内過激でguruな人たちから怖い突っ込みを貰いそうなので、後1回やったら止めます(笑) スクリプト言語なんて実際には適材適所で使うでしょうし、完全に個人的な興味でやっているだけなので。このテストをするためにPHP5を入れましたが、なかなか良い感触です。標準で付いてくるPHPはあまり役に立たないですからね。
スクリプトとテストの内容はvs. Rubyでやったのと同じです。PHP5 (cli) だと0.4秒ぐらいです。Perl の半分の時間ですが、PHPはcliでもあまり汎用性がないですから、それを考えるとやはりPerlは手放せませんね。
いや〜今度はPerlが圧勝しましたよん。でも勝ったのはXML::LibXMLのおかげかな。372キロバイトのXML文書からXPathであるノードを取ってきて別ファイルに出力するスクリプトをPerlとRuby書いてみました。PerlはCPANモジュールXML::LibXMLとXML::Parserを使用で、Rubyは付属のREXML使用です。結果はPerlが4回平均0.8秒。Rubyはな〜んと9秒もかかってしまいました。Rubyにももっと速いParserがあるのでしょうか。けっこうXMLの生データにコマンドラインから検索をかけたりすることが多いので、当分の間はRubyを忘れることにしました(笑) 文字列検索の速さには驚きましたけどね。まあ将来を睨んでぼちぼちいこかというところでしょうか。
ちょっちショックなことが。今まで文字列の検索と置換なんかをする簡単なスクリプトはPerlで書いていました。Perlの正規表現に慣れちゃうと、他のスクリプト言語やコマンドはあまり必要性が感じられないと言うのが主な理由ですが。で、なんとなく盲目的に文字列の置換などの速さはPerlが一番速いと思っていたのですが、どうも時代は変わってしまっていたようです。今日試しに8.6メガバイトのテキストを読み込んで全ての行(65681行)に出てくるある文字列を別のものに置換してテキストファイルに出力するという単純なスクリプトをPerlとRubyで書いてみました。置換に使ったのはPerlがs/XXX/YYY/gsでRubyはgsub(/XXX/, "YYY")です。Perlが最低でもきっかり1秒かかったのに対し、Rubyはなんと0.7〜0.8秒程で置換してしまいました。8回計測しましたが、結果は変わりませんでした。まだ試してませんが、これでコマンドラインでの使い勝手が良くて、XML処理でもPerlより速かったら、おそらくRubyに乗り換えます。でもPerlが好きなんだよなあ。
Apple | AppleScript & Automator | HTML | iPhone | japani-suomi | JavaScript | Mac | OSX | Suomeksi, koska en uskalla kirjoittaa japaniksi | Suomeksi, koska haluan kirjoittaa suomeksi | Suomeksi, koska tietsikka ei ymmärrä japania | suomi-japani | XML | おすすめサイト | お知らせ | ぼやき | ぼやき (フィンランド語版) | ウェブログ・ココログ関連 | クルマ | サッカー | スクリプト | スポーツ | ニュース | バイク | パソコン・インターネット | ファッション・アクセサリ | フィンランド | フィンランド人 | フィンランド語 | ユーミン | 北欧 | 悲劇 | 携帯・デジカメ | 文化・芸術 | 旅行・地域 | 日記・コラム・つぶやき | 映画・テレビ | 書籍・雑誌 | 私的Mac生活 | 経済・政治・国際 | 芸能・アイドル | 言語学 | 音楽
最近のコメント