複数のファイルをまとめて処理
複数のXMLファイルをまとめて処理したい場合は、次のようにする。
files = Dir.glob('C:¥KyotoCorpus4.0¥xml¥syn¥*.xml')
files.each do |file|
STDERR.puts file
doc = open(file){|f| Nokogiri::XML(f){|config| config.noblanks}}
(ここに処理を記述)
end
Dir.globはワイルドカードを含むファイル名を展開してファイル名のリスト(Array)で返す。STDERR.putsは標準エラー出力(普通はコマンドプロンプト)に文字列を出力する命令で、現在処理中のファイルの名前を画面に表示する。複数のファイルをまとめて処理する場合、処理が完了するまでに時間がかかって、ちゃんと動いているのか不安になることがあるので動作確認のために入れてある。上記のコードを関数として定義しておいて次のように使うこともできる。
def xml_enum(path)
files = Dir.glob(path)
files.each do |file|
STDERR.puts file
doc = open(file){|f| Nokogiri::XML(f){|config| config.noblanks}}
yield doc
end
end
path = 'C:¥KyotoCorpus4.0¥xml¥syn¥*.xml'
xml_enum(path) do |doc|
(ここに処理を記述)
end
さらに応用してクラスとして定義する場合。
class XMLFiles
include enumerable
def initialize(path)
@path = path
@files = Dir.glob(path)
end
def each
@files.each do |file|
doc = open(file){|f| Nokogiri::XML(f){|config| config.noblanks}}
yield doc
end
end
end
path = 'C:¥KyotoCorpus4.0¥xml¥syn¥*.xml'
corpora = XMLFiles.new(path)
corpora.each do |doc|
(ここに処理を記述)
end
このクラスはeachメソッドが定義されており、enumerableモジュールをインクルードしてあるが、to_aとかすると全てのXMLファイルを一度にメモリに読み込むことになるのでエラーに注意。mapとかで必要な情報だけを拾い集める分には問題ないと思われる。さらにファイルが複数あることを意識せずにxpath検索するメソッドを追加してみる。
class XMLFiles
def xpath(xpath)
self.each do |doc|
doc.xpath(xpath).each{|node| yield node}
end
end
end
corpora.xpath(xpath) do |node|
(ここに処理を記述)
end
Nokogiri::XML::Documentなどに対するxpathメソッドがNodeSetを返すのに対して、上記のxpathメソッドはeachのように動作する。複数のXML文書から集めたノードを集めて、それらのリストを返すようなメソッドとして定義してしまうと、メモリエラーになる可能性があるのでこのような方法を取っている。ノードセットに似たオブジェクトを返すようにしたいのであれば、次のようにEnumeratorを使う方法が考えられる。
class XMLFiles
def xpath(xpath)
enum = Enumerator.new do |y|
self.each do |doc|
doc.xpath(xpath).each{|node| y << node}
end
end
return enum
end
end
corpora.xpath(xpath).each do |node|
(ここに処理を記述)
end
この方法であれば、each以外のmapやcountなどのメソッドも利用することが可能になる。to_aするとメモリエラーになる可能性があるのは同じなので注意すること。
0 件のコメント:
コメントを投稿