2

サイズがかなり大きい XML ファイル (通常は 100 ~ 500 MB ですが、最大 1 GB) を効率的に編集して、特定の値を持つ属性を含まない要素をすべて削除しようとしています。速度の点でこれを実行する最も効率的な方法を探していますが、これは大きなファイルの問題であるため、大量のデータをメモリにロードしません。

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)

上記は、サイズが 100MB のテスト ファイルを約 10 秒で処理します。これを達成するためのより効率的な方法はありますか?

4

1 に答える 1

1

申し訳ありませんが、同等の巨大な xml ファイルが手元にないので、これらの提案を自分でベンチマークする必要があります… :-/

  1. これにcontextrootプロパティがあるため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.iterparsetag特定の要素のみを反復する引数があります。

    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 に答える