2014-10-05 50 views
0

我有以下格式的XML文件:的Python ElementTree的

<dir name="A"> 
    <dir name="B"> 
     <file name="foo.txt"/> 
    </dir> 
    <dir name="C"> 
     <dir name="D"> 
      <file name="bar.txt"/> 
     </dir> 
    </dir> 
</dir> 
<dir name="E"> 
    <file name="bat.txt"/> 
    <file name="cat.txt"/> 
</dir> 
<dir name="F"> 
    <dir name="G"> 
     <file name="dog.txt"/> 
     <file name="rabbit.txt"/> 
    </dir> 
</dir> 

我想使用Python ElementTree的模塊,以除去包含在其內部一個元件的任何元件。也就是說,我想獲取XML文件的內部元素(那些不包含其他元素的元素)以及他們的所有子元素。我想要任何這樣的元素被設置爲外層。例如,對於上面的XML文件,相應的輸出文件將爲:

<dir name="B"> 
    <file name="foo.txt"/> 
</dir> 
<dir name="D"> 
    <file name="bar.txt"/> 
</dir> 
<dir name="E"> 
    <file name="bat.txt"/> 
    <file name="cat.txt"/> 
</dir> 
<dir name="G"> 
    <file name="dog.txt"/> 
    <file name="rabbit.txt"/> 
</dir> 

我該如何實現這一目標?

+1

你嘗試過什麼嗎? – Beginner 2014-10-05 21:40:12

回答

0

公告中的元素的訪問順序,當你use iterparse - 這是一個深度優先搜索:

import xml.etree.ElementTree as ET 

with open('data', 'rb') as f: 
    context = ET.iterparse(f, events=('start', 'end')) 
    for event, elem in context: 
     if elem.tag == 'dir': 
      name = elem.get('name') 
      print(event, name) 

產量

('start', 'A') 
('start', 'B')  <-- ('start', 'B') is follow immediately by ('end', 'B') 
('end', 'B')  <-- 
('start', 'C') 
('start', 'D')  <-- start is follow immediately by end 
('end', 'D') 
('end', 'C') 
('end', 'A') 
('start', 'E')  <-- start is follow immediately by end 
('end', 'E') 
('start', 'F') 
('start', 'G')  <-- start is follow immediately by end 
('end', 'G') 
('end', 'F') 

AHAH,你正在尋找的元素 - 嵌套最深的dir元素 - 是首先用start事件先訪問並緊接着一個end事件(至少當我們只看dir El對此語句)。

因此,使用這種想法,我們就可以收集在一個新的root元素這些元素以獲得所需的XML:

root = ET.Element('root') 
previous_name = None 
with open('data', 'rb') as f: 
    context = ET.iterparse(f, events=('start', 'end')) 
    for event, elem in context: 
     if elem.tag == 'dir': 
      name = elem.get('name') 
      if event == 'start': 
       previous_name = name 
      elif previous_name == name: 
       root.append(elem) 
print(ET.tostring(root)) 

產量

<root><dir name="B"> 
     <file name="foo.txt" /> 
    </dir> 
    <dir name="D"> 
      <file name="bar.txt" /> 
     </dir> 
    <dir name="E"> 
    <file name="bat.txt" /> 
    <file name="cat.txt" /> 
</dir> 
<dir name="G"> 
     <file name="dog.txt" /> 
     <file name="rabbit.txt" /> 
    </dir> 
</root> 

注意,上面的iterparse代碼不明確iterparse訪問後的任何元素。如果你的XML很大,在不清除任何元素的情況下使用iterparse可能會使用太多的內存。在那種情況下,對於性能和更好的內存管理,我會使用lxml和fast_iter