4

Rubyでlibxmlを使用して、(のような)100万を超える小さな書誌レコードを含む大きなXMLファイルを読みたいと思います。<article>...</article>Readerクラスをメソッドと組み合わせてexpandレコードごとに読み取る方法を試しましたが、コードがメモリを消費するため、これが正しいアプローチかどうかはわかりません。したがって、私は一定のメモリ使用量でレコードごとに便利に処理する方法のレシピを探しています。以下は私のメインループです:

   File.open('dblp.xml') do |io|
      dblp = XML::Reader.io(io, :options => XML::Reader::SUBST_ENTITIES)
      pubFactory = PubFactory.new

      i = 0
      while dblp.read do
        case dblp.name
          when 'article', 'inproceedings', 'book': 
            pub = pubFactory.create(dblp.expand)
            i += 1
            puts pub
            pub = nil
            $stderr.puts i if i % 10000 == 0
            dblp.next
          when 'proceedings','incollection', 'phdthesis', 'mastersthesis':
            # ignore for now
            dblp.next 
          else
            # nothing
        end
      end  
    end

ここで重要なdblp.expandのは、サブツリー全体(<article>レコードなど)を読み取り、それを引数としてファクトリに渡してさらに処理することです。これは正しいアプローチですか?

次に、ファクトリメソッド内で、高レベルのXPathのような式を使用して、以下のように要素のコンテンツを抽出します。繰り返しますが、これは実行可能ですか?

def first(root, node)
    x = root.find(node).first
    x ? x.content : nil
end

pub.pages   = first(node,'pages') # node contains expanded node from dblp.expand
4

3 に答える 3

5

大きなXMLファイルを処理するときは、すべてをメモリにロードしないように、ストリームパーサーを使用する必要があります。2つの一般的なアプローチがあります。

  • SAXのようなプッシュパーサーでは、取得したタグに反応します(tadmanの回答を参照)。
  • プルパーサー。XMLファイル内の「カーソル」を制御します。このカーソルは、上下に移動するなどの単純なプリミティブを使用して移動できます。

プッシュパーサーは、一部のフィールドだけを取得する場合に使用すると便利だと思いますが、複雑なデータ抽出に使用するのは一般的に面倒であり、多くの場合、構文を使用して実装されますcase... when...

私の意見では、プルパーサーはツリーベースのモデルとプッシュパーサーの良い代替手段です。REXMLを使用したプルパーサーに関するDr.Dobbのジャーナルに素晴らしい記事があります。

于 2010-01-04T19:28:14.660 に答える
1

XMLを処理する場合、2つの一般的なオプションはツリーベースとイベントベースです。ツリーベースのアプローチは通常、XMLドキュメント全体を読み取り、大量のメモリを消費する可能性があります。イベントベースのアプローチは追加のメモリを使用しませんが、独自のハンドラロジックを作成しない限り何もしません。

イベントベースのモデルは、SAXスタイルのパーサーと派生実装で採用されています。

REXMLの例:http ://www.iro.umontreal.ca/~lapalme/ForestInsteadOfTheTrees/HTML/ch08s01.html

REXML:http ://ruby-doc.org/stdlib/libdoc/rexml/rdoc/index.html

于 2010-01-04T15:19:47.390 に答える
0

同じ問題が発生しましたが、Node#removeを呼び出すことで解決したと思います。展開されたノード上。あなたの場合、私はあなたが次のようなことをすべきだと思います

my_node = dblp.expand
[my_nodeで行う必要があることを実行してください]
dblp.next
my_node.remove!

なぜこれが機能するのかはよくわかりませんが、LibXML :: XML :: Reader#expandのソースを見ると、ノードの解放についてのコメントがあります。Reader#expandがノードをReaderに関連付けると推測しているので、Node#removeを呼び出す必要があります。それを解放します。

このハックがあっても、メモリ使用量はそれほど多くありませんでしたが、少なくともそれは増え続けませんでした。

于 2010-02-11T13:36:27.240 に答える