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秒
という圧倒的な差。
ま、完全に同じ処理というわけではないのですが、それにして圧倒的です




どうでもいいけど、「パース」じゃなくて「パーズ」ですよね。