2

我正在尝试有效地编辑相当大的 XML 文件(通常为 100-500MB,但最大为 1GB),以删除所有不包含具有给定值的属性的元素的出现。我正在寻找在速度方面执行此操作的最有效方法,同时也不会将大量数据加载到内存中,因为这对于较大的文件来说是一个问题。

使用示例 XML,结构如下所示,其中父元素可以相互嵌套任意次数。

<root>
<parent>
    <child id="c1">
        <content />
    </child>
    <child id="c2">
        <content />
    </child>
</parent>
<parent>
    <parent>
        <child id="c3">
            <content />
        </child>
    </parent>
</parent>
</root>

使用上面的示例 XML,我试图删除 ID 不等于“c1”的所有子元素,以给出以下结果:

<root>
<parent>
    <child id="c1">
        <content />
    </child>
</parent>
<parent>
    <parent />
</parent>
</root>

到目前为止,我想出的最有效的方法是使用 cElementTree iterparse:

import xml.etree.cElementTree as ET

xml_source = 'xml file location'
xml_output = 'xml output file location'

context = ET.iterparse(xml_source, events=("start", "end"))
context = iter(context)

event, root = context.next()

for event, elem in context:
    if event == 'end' and elem.tag == 'child' and elem.attrib['id'] != 'c1':
        elem.clear()

ET.ElementTree(root).write(xml_output)

以上将在大约 10 秒内处理大小为 100MB 的测试文件,有没有更有效的方法来实现这一点?

4

1 回答 1

1

抱歉,我手头没有巨大的等效 xml 文件,所以你必须自己对这些建议进行基准测试...... :-/

  1. context一个root属性,因此您只能iterparse在(默认)“结束”事件上:

    context = ET.iterparse(xml_source)
    
    for event, elem in context:
        if elem.tag == 'child' and elem.attrib['id'] != 'c1':
            elem.clear()
    
    ET.ElementTree(context.root).write(xml_output)    
    
  2. 使用lxml.etree而不是xml.etree

    import lxml.etree as ET
    
  3. lxml.etree.iterparse有一个tag参数只对特定元素进行迭代:

    context = ET.iterparse(xml_source, tag='child')
    
    for event, elem in context:
        if elem.attrib['id'] != 'c1':
            elem.clear()
    
  4. 最后一个建议,但不是关于速度。elem.clear()不会删除元素本身,只会清除其子元素、文本和尾部。所以你最终得到了空<child/>元素:

    <root>
    <parent>
        <child id="c1">
            <content />
        </child>
        <child />
    </parent>
    <parent>
        <parent>
            <child />
        </parent>
    </parent>
    </root>
    

    使用 lxml 您可以使用它来代替elem.clear()

    for event, elem in context:
        if elem.attrib['id'] != 'c1':
            elem.getparent().remove(elem)
    
于 2014-07-08T22:22:22.523 回答