• ベストアンサー

XML::XPathでのfor文の記述

たびたびお世話になります。よろしくお願いいたします。 perlでXML文書をHTMLへ変換しています。 ---------------------------------------- <!-- file.xml --> <root> <jouhou> <mei> <toushu>あ</toushu> <toushu>い</toushu> <toushu>う</toushu> </mei> <mei> <toushu>え</toushu> <toushu>お</toushu> <toushu>か</toushu> </mei> </jouhou> </root> ---------------------------------------- このようなXMLの場合に、XML::XPathで「あ」と「え」などの最初に来る<toushu>取りたいのですが 日によって<mei>の数が異なるため、<mei>の数を取得してその数でforをすることにしました。 が、うまくいきません。 my $xp = new XML::XPath( filename => "./file.xml" ); for(my $j=1;$j<=$xp->findvalue("count(//jouhou/mei/toushu)");$j++){ print $j; #←ここでは1と2が出ます。 print $xpath->find('//jouhou/mei[$j]/toushu')->get_node(1)->string_value; } このままでは全く出なく、["$j"]とすると、出るには出るのですが「あ」が2回出てしまいます。 書き方が違うと思うのですが、()でくくってみても出ません。 ちなみに print $xpath->find('//jouhou/mei[1]/toushu')->get_node($j)->string_value; だと、ちゃんと「あ」と「い」が出ました…。 もし、お分かりになれば教えていただきたいです。 お手数をおかけしますが、よろしくお願いいたします。

  • Perl
  • 回答数4
  • ありがとう数4

質問者が選んだベストアンサー

  • ベストアンサー
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.4

#1>「あ」と「え」が取り出したいです。 foreach my $node ($xp->find('//jouhou/mei/toushu[1]')->get_nodelist){ print $node->string_value)."\n"; } こんな感じで、 あと、蛇足ですけど for の条件で >$j<=$xp->findvalue("count(//jouhou/mei/toushu)"); のようにすると、条件判定の度に同じことをやり直させることになります。 こういう場合は、件数はループの外で求めるようにした方がよいでしょう。 また、 print $xpath->find("//jouhou/mei[$j]/toushu")->get_node(1)->string_value; でも、一からやり直しさせることになります。 こういう場合は、ノードの(検索結果)リストを作って、それぞれのノードについてループで処理させます。

tako-kani
質問者

お礼

お世話になっております。 回答ありがとうございました。 やっとプログラムが完成しつつあるのですが、やはり実行すると速度が遅く、無駄なところが無いか見直していたので、 蛇足で書いていただいたことがとても参考になりそうです。というか、ぜひ参考にさせていただきます。 ありがとうございました。 また、何かありましたらよろしくお願いします。

tako-kani
質問者

補足

お礼を先に書いてしまったので、補足にて失礼します。 書いていただいた内容を全て試させていただいたところ、すごく早くなりました!! Benchmarkで調べてみたら10秒くらい違いました。 本当にありがとうございました。

その他の回答 (3)

  • pipipi523
  • ベストアンサー率40% (148/365)
回答No.3

「最初の」を見逃しました・・ my $xp = new XML::XPath( filename => "./file.xml" ); for(my $j=1;$i<=$xp->findvalue("count(//jouhou/mei)");$j++){ print $xp->find("//jouhou/mei[$j]/toushu")->get_node(1)->string_value; } ※結果的に ・"count(//jouhou/mei/toushu)"→"count(//jouhou/mei)" ・$xpath→$xp ・'//jouhou/mei[$j]/toushu'→"//jouhou/mei[$j]/toushu" の3点が違いました。

tako-kani
質問者

お礼

たびたびの回答ありがとうございます。 本当に助かりました。 また、何かありましたらよろしくお願いします。

  • pipipi523
  • ベストアンサー率40% (148/365)
回答No.2

シングルクオート''で括ると展開されないのでダメなのだと思います こんな感じではないでしょうか? my $xp = new XML::XPath( filename => "./file.xml" ); for(my $i=1;$i<=$xp->findvalue("count(//jouhou/mei)");$i++){ for(my $j=1;$j<=$xp->findvalue("count(//jouhou/mei[$i]/toushu)");$j++){ print $xp->find("//jouhou/mei[$i]/toushu")->get_node("$j")->string_value; } }

tako-kani
質問者

お礼

試してみたら""でうまくいきました。 ありがとうございました。

tako-kani
質問者

補足

あ!!そうですよね!そうかもしれないです。 ずっと$jばかりに目が行っていて、気が付きませんでした。 ありがとうございます。

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.1

すいません、結局ループで何を取り出したいのかがよくわかりませんでした。 ・あ,えを取り出したい。 ・あ,い,うを取り出したい。 ・もっと別

tako-kani
質問者

補足

書き方が悪くて申し訳ありません…。 「あ」と「え」が取り出したいです。

関連するQ&A

  • XML::XPathを使う

    エラーがでて期待する出力が出ず困っています。 ファイル名とXPathのコマンドライン引数をとり、 指定されたパスに一致するノードを出力するものです。 (参考書の写しなので、記述ミスはないと思います。) ■grabber.pl use XML::XPath; use XML::XPath::XMLParser; my $xpath = XML::XPath->new( filename => shift @ARGV ); my $nodeset = $xpath->find( shift @ARGV ); foreach my $node ( nodeset->get_nodelist ) { print XML::XPath::XMLParser::as_string( $node ) . "\n"; } コマンドラインに入力しているものは、 perl grabber.pl data.xml "/inventory/category/item/name" ■data.xml <?xml version="1.0"?> <inventory date="2001"> <category> <item id="2"> <name>aaaa</name> </item> </category> </inventory> です。 perl grabber.pl data.xml "/inventory/category/item/name" としても、下記にエラーが出ます。 Can't locate object method "get_nodelist" via package "nodeset" (perhaps you for got to load "nodeset"?) at grabber.pl line 5. いろいろ試してみたり調べたのですが、 行き詰ってしまいました。 期待する出力は"<name>aaaa</name>"なのですが・・・ どなたかこのエラーの原因がおわかりになるかた いらっしゃいますでしょうか。 環境は以下になります。 ・windowsxp ・activePerl モジュールのインストール済み ・XML-XPath ・XML-Parser 以上です。

    • ベストアンサー
    • Perl
  • XMLのXPathについて

    下記のようなXMLがあるとして、XPathでdataノードを取得する場合、 /root/node/dataと/root/dataの二通りがあると思いますが、これを一つにまとめて書くことは可能でしょうか? また、dataノードに辿りつくのに、途中のパスがすべて把握できない場合(/root/AAA/data、/root/BBB/CCC/data。。。) XPathでdataノードを取得することができますでしょうか? GetElementsByTagName("data")にした方が良いでしょうか? <?xml version="1.0" encoding="UTF-8"?> <root> <node> <data>データ1です</data> </node> <node> <data>データ2です</data> </node> <data>データ3です</data> </root>

    • 締切済み
    • XML
  • XML::XPath でXMLを修正して出力するには?

    XML::XPath を使って以下のようなソースを書きましたが、出力の方法がわかりません。 ++++++++++++++++ use strict; use XML::XPath; my $filename = "test.html"; my $xp = XML::XPath->new(filename=>$filename); my @nodeset = $xp->findnodes("//div[attribute::class='center']//img[attribute::class='right'"); for my $node ( @nodeset ){ print $node->removeAttribute('class'), "\n"; } print $xp; 1; ++++++++++++++++++++++++++++ 最後の print $xp のところで、修正後のXMLを出力したいです。 修正後のXMLをテキストとして出力(変数に入れるでも、標準出力でも)するには、どうすればよいのでしょうか?

    • ベストアンサー
    • Perl
  • ->の意味

    よく->といったものが、 参考スクリプトなどをみていると、 でてくるのですが、いまいちどう 解釈してよいのやらわかりません。 調べてもなかなかみつからず、質問させていただきます。 use XML::XPath; use XML::XPath::XMLParser; my $xpath = XML::XPath->new( filename => shift @ARGV ); my $nodeset = $xpath->find( shift @ARGV ); foreach my $node ( $nodeset->get_nodelist ) { print XML::XPath::XMLParser::as_string( $node ) . "\n"; } XML::XPath->new( filename => shift @ARGV );とか $xpath->find( shift @ARGV );とか $nodeset->get_nodelistは どういった理解をすればよいのでしょうか。 教えていただけますでしょうか。

    • ベストアンサー
    • Perl
  • XML::XPath -- 追加したノードが見付からない

    XMLのテンプレートを既存のXMLに追加したのですが、 その後 findnodes() で検索しても合致しません。 XML::XPath::Node::Element->new() を使って作成したものは 合致してくれます。 XML::XPath::XMLParser でパースしたものを追加して findnodes() で合致させるにはどのようにしたらよいのでしょうか。 ------------------------------- use XML::XPath; #### 元のXML my $xmldata = <<EOM; <?xml version="1.0" encoding="UTF-8" ?> <list>   <item>orange</item>   <item>apple</item>   <item>lemon</item> </list> EOM ### 追加するXMLのテンプレート my $xmlappend = <<EOM;   <item>pine</item> EOM ## 追加先ノードを取り出す my $xml = XML::XPath->new( xml=>$xmldata ); my ($list) = $xml->findnodes('/list'); ## 追加用XMLを作成して追加 my $append = XML::XPath::XMLParser->new( xml=>$xmlappend )->parse; $list->appendChild( $append ); ## もういっこ追加。こちらはこの場で作る $newnode = XML::XPath::Node::Element->new('item'); $newtext = XML::XPath::Node::Text->new('banana'); $newnode->appendChild( $newtext ); $list->appendChild( $newnode ); ## 現状確認 → pine は入っていた print $list->toString."\n"; ## item一覧を取得 @nodes = $xml->findnodes('/list/item'); ## 一覧を出力 → 追加した pine が出力されない。 bananaはある。 map{ print $_->toString."\n" } @nodes;

    • ベストアンサー
    • Perl
  • PerlによるXMLファイルの解析&出力

    XMLファイルで以下のようなXMLファイルから、 <?xml version="1.0" encoding="Shift_JIS"?> <class3> <Personal> <No>1</No> <Name>相上男</Name> <phone>00-0000</phone> </Personal> <Personal> <No>2</No> <Name>柿句毛子</Name> <phone>11-1111</phone> </Personal> </class3> perlでNameの部分のタグだけ抜き出しXMLファイルに出力するプログラムを組み立てたいです。自分でも以下のようなプログラムを組み立てたのですが、 #!/usr/bin/perl use strict; use Encode; use XML::XPath; use XML::XPath::XMLParser; # 書き込み用にファイルを開く open( OUTPUTFILE, ">Output2.xml" ); # 標準出力に書き出し print &xml_xpath; # ファイルを出力先に設定 select( OUTPUTFILE ); # 出力先を元に戻す select( STDOUT ); # ファイルを閉じる close( OUTPUTFILE ); sub xml_xpath{ my $file = "class3.xml"; my $xp = XML::XPath->new(filename => $file); foreach my $node( $xp->find('/class3/Personal/Name')->get_nodelist){ print Encode::encode("shift_jis", $node )."\n"; } } XML::XPath::Node::Element=REF(0x1036c58c) XML::XPath::Node::Element=REF(0x1036cb8c) と、出てくるだけで動きません。ほとんど初心者なのでまったく見当違いのプログラムを組み立てているかもしれませんが、よろしくおねがいします。

  • XML::DOM / XML::XPathでソート

    XML::DOMで効率的なソートの方法はどんなものがありますでしょうか。 一応動くものは作れたのですが、効率的とは言い難く、しかも ソート項目が一意のデータでないといけないという欠点があります。 ############################################### use XML::DOM::XPath; my $xml = <<EOM; <?xml version="1.0" encoding="UTF-8" ?> <list> <item id="10">Apple</item> <item id="5">Orange</item> <item id="20">Melon</item> </list> EOM my $parser = XML::DOM::Parser->new(); my $doc = $parser->parse( $xml ); ## <item>タグの一覧を作成 my @list = $doc->findnodes('/list/item'); ## <item>タグ id属性一覧を作成 my @idlist = map{ $_->getAttribute("id") } @list; ## id属性順にソート foreach my $id ( sort{ $a<=>$b } @idlist ){ ## id属性値を指定してノードリストを取得 my @item = $doc->findnodes('/list/item[@id='.$id.']'); ## idは一意なのでリストの先頭で固定 print $item[0]->getAttribute('id')."\n"; print $item[0]->getFirstChild->getNodeValue."\n"; } ############################################### ハッシュなら sort{ $hoe{$b} <=> $hoe{$a} } keys %hoe といった方法があるのですが、XML::DOMの場合は同じようにいきません。 良い方法がありましたらお願いします。

    • ベストアンサー
    • Perl
  • XML::Simpleの使い方について

    下記のような、プログラムを書いたのですが、上のタイトルはとれるのですが、連続して出てくるentryがどうしても取得できません。 何かご助言頂ければありがたいです。 my $url = "http://blog.livedoor.jp/takapon_ceo/atom.xml"; my $xml = get($url); my $tree = XMLin($xml); my $title = $tree->{title}; print "$title"; for (my $i = 0; $i <= 5; $i++) { $id[$i] = $tree->{'entry'}->[$i]->{'id'}; print "$id[$i]"; } よろしくお願いします。

    • ベストアンサー
    • Perl
  • vb6 XMLファイル出力について<S></S>

    vb6にてXMLファイル出力のプログラムを作成しています。 XMLファイル内容 <?xml version="1.0" encoding="UTF-8"?> <root> <key>AA</key> <string>aa</string> <key>BB</key> <string/> </root> 上記の<string/>の部分を<string></string>のように出力したいのですが、 どうしても上手くいきません。 下記にソースを記載します。 何か少しでもお分かりになる情報がありましたらご教授願いします。 よろしくお願いします。 Dim xmlDoc As MSXML2.DOMDocument 'XMLドキュメント Dim xmlPI As IXMLDOMProcessingInstruction 'XML宣言 Dim node(50) As IXMLDOMNode '要素 Dim attr As MSXML2.IXMLDOMAttribute '属性 'XMLドキュメントを作成 Set xmlDoc = New MSXML2.DOMDocument 'XML宣言を追加 Set xmlPI = xmlDoc.appendChild(xmlDoc.createProcessingInstruction("xml", "version=""1.0"" encoding=""UTF-8""")) '<root>要素を追加 Set node(1) = xmlDoc.appendChild(xmlDoc.createNode(NODE_ELEMENT, "root", "")) '<key>要素を追加 Set node(2) = node(1).appendChild(xmlDoc.createNode(NODE_ELEMENT, "key", "")) node(2).Text = "AA" '<string>要素を追加 Set node(3) = node(1).appendChild(xmlDoc.createNode(NODE_ELEMENT, "string", "")) node(3).Text = "aa" '<key>要素を追加 Set node(4) = node(1).appendChild(xmlDoc.createNode(NODE_ELEMENT, "key", "")) node(4).Text = "BB" '<string>要素を追加 Set node(5) = node(1).appendChild(xmlDoc.createNode(NODE_ELEMENT, "string", "")) node(5).Text = "" 'XMLドキュメントの出力 Dim strXMLFile As String strXMLFile = Format(Date, "yyyymmdd") & Format(Time, "hhmmss") xmlDoc.save ("C:TEST\TEST.xml") '終了処理 If Not xmlDoc Is Nothing Then Set xmlDoc = Nothing If Not xmlPI Is Nothing Then Set xmlPI = Nothing

  • ScalaでXMLファイルからノードを選択する.

    ScalaでXMLファイルから次のようなXPath式で表されるのノードを選択したくてサンプルプログラムを作ってみました. XPath式:"/musicLibrary/cd[string(year) = '1994']/title" [サンプルプログラム] import scala.xml.XML import scala.xml.Node import scala.xml.Elem object Main { def main(args: Array[String]): Unit = { val root:Elem = XML.loadFile("MusicLibrary.xml") val cds = ((root \\ "musicLibrary" \ "cd") .filter(p => p.asInstanceOf[Elem].child .filter(child => isElem(child) &&(child.asInstanceOf[Elem].label == "year")) .exists(year => year.text == "1994"))) val titles = cds \ "title" titles.foreach(title => println("The title='" + title.text + "'")) } def isElem(node:Node) ={ node match{ case e:Elem => true case _ => false } } } このプログラムで入力XMLファイル(MusicLibrary.xml)の以下が部分が選択され、 <?xml version="1.0" encoding="utf-8"?> <musicLibrary> ... <cd id="4"> <title>The Dark Side of the Moon</title> <year>1994</year> <artist>Pink Floyd</artist> <genre>Rock</genre> </cd> ... </musicLibrary> 一応 The title='The Dark Side of the Moon' とコンソール表示されます. しかし、まったくの初心者のプログラムのため val cds = 以下とval titles=以下は非常に冗長に感じられます. もっとシンプルに目的のノードを選択できるコーディングがありましたらご教授ください. 以上 よろしくお願いいたします.