2009-10-03 2 views

答えて

6

"オンザフライ"で解析とドキュメントのツリーは実際には互換性がありません。 SAXスタイルのパーサが通常これに使用されます(たとえば、Pythonの標準xml.sax)。 startElement、endElementなどのさまざまなイベントのハンドラを持つクラスを定義する必要があります。パーサは、XMLファイルを解析するときにメソッドを呼び出します。

+1

これは私が欲しいものです...「開始タグ」などのイベントに「反応する」必要はありません。 – jldupont

+1

@ Jean-Lou:ツリー全体を必要としない場合、SAXは行く道。これは、コンテンツのツリーの代わりにイベントのストリームとしてドキュメントを処理するために作成されています。 –

4

PullDomあなたは何をしますか? SAXのようなストリームからXMLを読み込みますが、選択した部分のDOMを作成します。

"PullDOMは、モノリシックツリーではなく、ストリーミング(効率的な)方法でDOMオブジェクトを操作するための本当にシンプルなAPIです。

+0

forループに「yield」文を入れるとfor(event、node)for events:yield(event、node)}私はforループに入る次回の開始時にPullDomは再起動しませんか? – jldupont

+0

...それは "iterparse"で起きることなので... – jldupont

+0

@ Jean-Lou Dupont:イテレータの動作が必要な場合は、おそらくElementTreeオブジェクトでiter(...)を呼び出す必要がありますか? – u0b34a0f6ae

15

xml.etree.cElementTreeは正しい使い方でジェネレータに近づきます。デフォルトでは、 'end'イベントの後に各要素を受け取ります。その時点で、それを処理することができます。処理後に要素を必要としない場合は、要素に対してelement.clear()を使用する必要があります。それによってメモリを節約します。


ここでは、リズムボックス(音楽プレーヤー)ライブラリを解析する完全な例を示します。私は(c)ElementTreeのiterparseを使用し、各処理された要素に対して私はかなり多くのメモリを節約するためにelement.clear()を呼び出します。 (Btw、以下のコードは、同じことをするいくつかのサックスコードの後継ですcElementTreeソリューションは、1)以来、安心していましたコードは簡潔で、何も必要以上を表現します2)3倍速く、3)それは、より少ないメモリを使用しています。)

import os 
import xml.etree.cElementTree as ElementTree 
NEEDED_KEYS= set(("title", "artist", "album", "track-number", "location",)) 

def _lookup_string(string, strmap): 
    """Look up @string in the string map, 
    and return the copy in the map. 

    If not found, update the map with the string. 
    """ 
    string = string or "" 
    try: 
     return strmap[string] 
    except KeyError: 
     strmap[string] = string 
     return string 

def get_rhythmbox_songs(dbfile, typ="song", keys=NEEDED_KEYS): 
    """Return a list of info dictionaries for all songs 
    in a Rhythmbox library database file, with dictionary 
    keys as given in @keys. 
    """ 
    rhythmbox_dbfile = os.path.expanduser(dbfile) 

    lSongs = [] 
    strmap = {} 

    # Parse with iterparse; we get the elements when 
    # they are finished, and can remove them directly after use. 

    for event, entry in ElementTree.iterparse(rhythmbox_dbfile): 
     if not (entry.tag == ("entry") and entry.get("type") == typ): 
      continue 
     info = {} 
     for child in entry.getchildren(): 
      if child.tag in keys: 
       tag = _lookup_string(child.tag, strmap) 
       text = _lookup_string(child.text, strmap) 
       info[tag] = text 
     lSongs.append(info) 
     entry.clear() 
    return lSongs 

さて、私はあなたの期待を理解していない、あなたは以下の期待を持っていますか?

# take one 
for event, entry in ElementTree.iterparse(rhythmbox_dbfile): 
    # parse some entries, then exit loop 

# take two 
for event, entry in ElementTree.iterparse(rhythmbox_dbfile): 
    # parse the rest of entries 

iterparseを呼び出すたびに、新しいイテレータオブジェクトが取得され、新しいファイルが読み込まれます。あなたはイテレータのセマンティクスを持つ永続オブジェクトをしたい場合は、両方のループ(未試行コード)内の同じオブジェクトを参照する必要があります。

#setup 
parseiter = iter(ElementTree.iterparse(rhythmbox_dbfile)) 
# take one 
for event, entry in parseiter: 
    # parse some entries, then exit loop 

# take two 
for event, entry in parseiter: 
    # parse the rest of entries 

私は別のオブジェクトが異なる意味を持っているので、それは混乱することができると思います。ファイルオブジェクトは常に内部状態を持ち、ファイル内を進行しますが、繰り返し実行します。 ElementTree iterparseオブジェクトは明らかにそうではありません。要点は、forループを使用するとき、forはiterateを繰り返すとiter()を常に呼び出すと考えることです。ここではファイルオブジェクトとElementTree.iterparseを比較した実験である:

>>> import xml.etree.cElementTree as ElementTree 
>>> pth = "/home/ulrik/.local/share/rhythmbox/rhythmdb.xml" 
>>> iterparse = ElementTree.iterparse(pth) 
>>> iterparse 
<iterparse object at 0x483a0890> 
>>> iter(iterparse) 
<generator object at 0x483a2f08> 
>>> iter(iterparse) 
<generator object at 0x483a6468> 
>>> f = open(pth, "r") 
>>> f 
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98> 
>>> iter(f) 
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98> 
>>> iter(f) 
<open file '/home/ulrik/.local/share/rhythmbox/rhythmdb.xml', mode 'r' at 0x4809af98> 

何を参照してくださいすることはiterparseオブジェクト上のITERへの各呼び出しは()新しいジェネレータを返すということです。しかし、ファイルオブジェクトは、保存されなければならない内部オペレーティングシステム状態とそれ自身のイテレータを持っています。

+0

@kaizer:実際には、forループがelement.clear()の後に入力されるたびに、ドキュメントのサブセットを扱うようなものでしょうか? – jldupont

+0

あなたは何をしたいのかを定義しておらず、あなたの期待は私を驚かせます。私はiterparseを1つのforループでドキュメント全体にわたって使用します。私は例を挙げます。 – u0b34a0f6ae

+0

@kaizer:あなたの努力のために多くの感謝。私はこの記事のおかげでSAXパーサを発見しました。このアプローチでステートマシンベースのパーサーをきちんと構築することができるようです。 (私はXML-newbieだと教えてもらえますか?;-) – jldupont

0

これはElementTreeのと増分解析で可能です:SAXよりも使い http://effbot.org/zone/element-iterparse.htm#incremental-parsing

import xml.etree.cElementTree as etree 
for event, elem in etree.iterparse(source): 
    ... 

簡単。

+0

@jldupont:あなたの質問には、2年前にそれを試したことが書かれています: "" "xml.etree.cElementTree"(それは本当に素敵です) "iterparse"を試しました "" " –

+0

-1大きなファイルcElementTreeを使用してください(OP状態は既に試されています!)...あなたは@ kaiser.seの答えを読んでいませんか? –

関連する問題