2009-10-03 71 views

回答

6

“即时”解析和文档树并不真正兼容。 SAX风格的解析器通常用于此(例如,Python的标准xml.sax)。你基本上必须用startElement,endElement等各种事件的处理程序定义一个类,解析器将在解析XML文件时调用这些方法。

+1

这就是我想要的......我不介意对“开始标记”等事件做出“反应”。 – jldupont 2009-10-03 12:21:52

+1

@ Jean-Lou:如果你不需要整棵树,那么SAX就是要走的路。它用于将文档处理为事件流而不是内容树。 – 2009-10-03 13:32:10

4

PullDom做你想要的。它从流中读取XML,如SAX,但是随后为其中选定的一部分构建DOM。

“PullDOM是一个非常简单的API,用于以流(高效!)方式处理DOM对象,而不是一个单一的树。”

+0

所以如果我在for循环中加入“yield”语句(例如for事件中的(event,node):yield(event,node)}下次我进入for-loop时,PullDom不会重新启动? – jldupont 2009-10-03 12:33:37

+0

...因为这就是“iterparse”发生的事情...... – jldupont 2009-10-03 12:34:33

+0

@ Jean-Lou Dupont:如果你想要迭代器行为,也许你应该在ElementTree对象上调用'iter(...)'? – u0b34a0f6ae 2009-10-03 12:41:45

15

xml.etree.cElementTree靠近正确用法的发电机;默认情况下,您会在“结束”事件之后收到每个元素,此时您可以处理它。如果处理后不需要它,则应该在元素上使用element.clear();从而节省内存。


这里是一个完整的例子,我的意思是,我解析Rhythmbox(音乐播放器)库。我使用(c)ElementTree的iterparse,并为每个处理过的元素调用element.clear(),这样我可以节省大量的内存。 (顺便说一句,下面的代码是一些萨克斯代码的继承者做同样的事情; 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总是在你迭代的东西上调用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> 

你看到的是每次调用ITER()的iterparse对象返回一个新的发电机。但是,该文件对象具有必须保留的内部操作系统状态,并且它具有自己的迭代器。

+0

@kaizer:实际上,它就好像每次在for循环输入element.clear()之后处理文档的子集? – jldupont 2009-10-03 12:43:23

+0

你还没有定义你想要做什么,你的期望让我感到惊讶;我将在整个文档中使用iterparse作为循环。我会举个例子。 – u0b34a0f6ae 2009-10-03 13:27:33

+0

@kaizer:非常感谢您的所有努力。感谢这篇文章,我发现了SAX解析器,看起来我可以用这种方法整洁地构建基于状态机的解析器。 (你能告诉我我是一个XML新手?;-) – jldupont 2009-10-03 15:24:04

0

这是可能的ElementTree的和增量解析: http://effbot.org/zone/element-iterparse.htm#incremental-parsing

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

更容易比SAX使用。

+0

@jldupont:你的问题说你尝试过(两年前):“”“我试过”xml.etree.cElementTree“的iterparse(这真的很不错)”“” – 2012-01-01 22:16:38

+0

-1 Large file means使用cElementTree(OP的状态已经被使用了!)...你没有阅读@ kaiser.se的答案吗? – 2012-01-01 22:18:52