nimを使ってXMLファイルをパースする為の日本語情報があまりないので、「xmlparser」モジュールを使ってのXMLファイルのパースについて記事にします
色々苦労しましたが、なんとか思い通りの動きができるようになりました
目次
xmlparserモジュールの仕様
以下のリンクにて説明があります(英語)
XMLParserの仕様
使用例
といっても、所詮英語…一応以下のような仕様です
xmlをロードする
import os, streams, parsexml, strutils var filename = "something.xml" var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) var x: XmlParser open(x, s, filename)
XmlParserがFileStreamをオ−プンしています
ノードを読み込む
xmlparserは、nextで次のノードに移ります
ループさせて、EOFまで読み込みます
xp.kindによって処理を分岐させる形になります
xp.next block mainLoop: while true: case xp.kind of xmlElementStart: echo "element Start!" xp.next of xmlElementOpen: echo "element Open!" xp.next of xmlEof: break # end of file reached of xmlError: echo(errorMsg(xp)) xp.next() else: xp.next() # skip other events
上記の例だと、EOF(xmlEof)まで無限ループを回して、xmlEofを見つけたら、ブレイクするという流れになります
kindは、以下のようなものがあります。よく使うものから順に紹介すると以下の通り
kindの名前 | 内容 | 例 |
---|---|---|
xmlElementStart | XMLの構成要素の開始を示します(”>”までを取得 | < elem > |
xmlElementEnd | XMLの構成要素の終了そ示す(”>”までを取得 | < /elem > |
xmlElementOpen | XMLの構成要素の開始を示す(”>”までを取得しない | < /elem |
xmlElementClose | Openで取得した構成要素の”>”を取得する | > |
xmlAttribute | Openで取得した構成要素の中にあるアトリビュートを取得 | key=”value” |
xmlCharData | 文字列データ | テキスト |
xmlError | エラーが起きたときの | N/A |
xmlEOF | ファイルの終了 | N/A |
詳細は、以下の英語の説明にあるのですが、とりあえず上記さえあれば、なんとかコントロールできます
xmlElementStartとxmlElementOpenの違い
実は、最初この2つの使い方が理解できなかったのですが、基本的には以下のような違いのようです
例えば、以下のようなXMLファイルがあったとします
劇団名 公演名a 2018/01/01 2018/01/3 公演名b 2018/02/01 2018/02/28
この場合に、kind==xmlElementStartで得られた構成要素の名前(elementName)を抽出すると以下のように取れます
取得するソースは、以下の通り
import os, streams, parsexml, strutils if paramCount() < 1: quit("Usage: htmltitle filename[.html]") var filename = addFileExt(paramStr(1), "xml") var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) var x: XmlParser open(x, s, filename) while true: x.next() case x.kind of xmlElementStart: echo x.elementName of xmlEof: break # end of file reached else: discard # ignore other events x.close()
上記のソースコードで実行結果は、以下のようになります
xml company name shows title fromdate todate title fromdate todate
見ていただくと気づきますが、showは、抽出されていません。これは、showはアトリビュート「id」を保有しているためです
アトリビュートを保有する項目は、「xmlElementOpen」で分岐する必要があります
上記のコードを以下のように修正して実行します
import os, streams, parsexml, strutils if paramCount() < 1: quit("Usage: htmltitle filename[.html]") var filename = addFileExt(paramStr(1), "xml") var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) var x: XmlParser open(x, s, filename) while true: x.next() case x.kind of xmlElementOpen: echo "open:" & x.elementName of xmlElementStart: echo "start:" & x.elementName of xmlEof: break # end of file reached else: discard # ignore other events x.close()
上記のコードの実行結果は以下のとおりです
start:xml start:company start:name start:shows open:show start:title start:fromdate start:todate open:show start:title start:fromdate start:todate
この通り、この「xmlElementOpen」と「xmlElementStart」を両方キャッチしないとXMLの構成要素を全て取得することができません
要素のテキストを取得するのは、kindがCharDataであるときに取得する
上記のように、要素を特定したあとは、その要素の内容を取得します
xp.next() #xmlparserの次へノードを動かす if xp.kind==xmlCharData: #次のノードが文字列データであることを確認 value = xp.charData #文字列であれば、値に設定する else: value = ""
上記のコマンドの組み合わせで、取り込めるようになりました
以上 nimでXMLファイルをパースして情報を取り込むための初歩の記事でした
PSちなみに、同様の処理をRubyとnimで実装した場合の速度比較は以下
3248のXMLファイルのパース処理
Ruby:18分4秒(4320秒)
nim :13秒
という圧倒的な差。
ま、完全に同じ処理というわけではないのですが、それにして圧倒的です
どうでもいいけど、「パース」じゃなくて「パーズ」ですよね。