我想解析一个xml文件,然后通过删除所选元素来处理结果树.我的问题是删除一个元素会破坏迭代元素的循环.
考虑以下xml数据:
<results> <group> <a /> <b /> <c /> </group> </results>
和代码:
import xml.etree.ElementTree as ET def showGroup(group,s): print(s + ' len=' + str(len(group))) print('<group>' ) for e in group: print(' <' + e.tag + '>') print('</group>\n') def processGroup(group): for e in group: if e.tag != 'a': group.remove(e) showGroup(group,'removed <' + e.tag + '>') tree = ET.parse('x.xml') root = tree.getroot() for group in root: processGroup(group)
我期望for循环处理元素< a>,< b>和< c>为了.尤其是:
>处理< a>不应该删除任何元素
>处理< b>应删除< b>
>处理< c>应该删除< c>
我希望生成的树在< group>内有一个元素. (< a>元素),len(组)将返回1.
相反,在处理< b>之后,for循环决定已经满足结束测试,并且它不处理元素< c>.如果是,则< c>将被删除.相反,我留下了一个元素< a>的树.和< c>,len(组)返回2.
在删除所选元素时,我需要做什么来处理所有三个元素? PS:欢迎任何关于风格的评论或更好的做事方式.
更新:如果在删除元素后没有代码,那么丑陋的黑客会以某种效率为代价“修复”问题.但在我的真实程序中,修剪循环后会有很多代码.
for e in group: if e.tag != 'a': group.remove(e) showGroup(group,'removed <' + e.tag + '>') processGroup(group)
我假设如果for循环中断,那么在开始时再次启动组可能会解决问题.递归是一种整洁的方式 – 以重新处理已经检查但未被删除的所有元素为代价.
我对这个解决方案不满意.
问题是你正在从正在迭代的东西中删除元素,当你删除一个元素时,剩下的元素会被移位,所以你最终可能会删除不正确的元素:
一个简单的解决方案是遍历树的副本或使用反向:
复制:
def processGroup(group): # creates a shallow copy so we are removing from the original # but iterating over a copy. for e in group[:]: if e.tag != 'a': group.remove(e) showGroup(group,'removed <' + e.tag + '>')
逆转:
def processGroup(group): # starts at the end,as the container shrinks. # when an element is removed,we still see # elements at the same position when we started out loop. for e in reversed(group): if e.tag != 'a': group.remove(e) showGroup(group,'removed <' + e.tag + '>')
使用复制逻辑:
In [7]: tree = ET.parse('test.xml') In [8]: root = tree.getroot() In [9]: for group in root: ...: processGroup(group) ...: removed <b> len=2 <group> <a> <c> </group> removed <c> len=1 <group> <a> </group>
您也可以使用ET.tostring代替for循环:
import xml.etree.ElementTree as ET def show_group(group,s): print(s + ' len=' + str(len(group))) print(ET.tostring(group)) def process_group(group): for e in group[:]: if e.tag != 'a': group.remove(e) show_group(group,'removed <' + e.tag + '>') tree = ET.parse('test.xml') root = tree.getroot() for group in root.findall(".//group"): process_group(group)