2011-05-04 62 views
2

我需要解析一些XML拉出嵌入式模板標籤進行進一步分析。不過,我似乎無法彎曲Python的正則表達式來做我想做的事情。正則表達式來解析模板標籤中的XML

英語:當一個模板標籤包含的行中的任何地方,刪除所有的XML在特定行並在其位置只留下模板標籤。

我放在一起測試案例來證明。下面是原始的XML:

<!-- regex_trial.xml --> 
<w:tbl> 
    <w:tr> 
     <w:tc><w:t>Header 1</w:t></w:tc> 
     <w:tc><w:t>Header 2</w:t></w:tc> 
     <w:tc><w:t>Header 3</w:t></w:tc> 
    </w:tr> 
    <w:tr> 
     <w:tc><w:t>{% for i in items %}</w:t></w:tc> 
     <w:tc><w:t></w:t></w:tc> 
     <w:tc><w:t></w:t></w:tc> 
    </w:tr> 
    <w:tr> 
     <w:tc><w:t>{{ i.field1 }}</w:t></w:tc> 
     <w:tc><w:t>{{ i.field2 }}</w:t></w:tc> 
     <w:tc><w:t>{{ i.field3 }}</w:t></w:tc> 
    </w:tr> 
    <w:tr> 
     <w:tc><w:t>{% endfor %}</w:t></w:tc> 
     <w:tc><w:t></w:t></w:tc> 
     <w:tc><w:t></w:t></w:tc> 
    </w:tr> 
</w:tbl> 

這是所需結果:

<!-- regex_desired_result.xml --> 
<w:tbl> 
    <w:tr> 
     <w:tc><w:t>Header 1</w:t></w:tc> 
     <w:tc><w:t>Header 2</w:t></w:tc> 
     <w:tc><w:t>Header 3</w:t></w:tc> 
    </w:tr> 
    {% for i in items %} 
    <w:tr> 
     <w:tc><w:t>{{ i.field1 }}</w:t></w:tc> 
     <w:tc><w:t>{{ i.field2 }}</w:t></w:tc> 
     <w:tc><w:t>{{ i.field3 }}</w:t></w:tc> 
    </w:tr> 
    {% endfor %} 
</w:tbl> 

這裏是我用來測試一些Python代碼:

#!/usr/bin/env python 
import re 
f = open('regex_trial.xml', 'r') 
orig_xml = f.read() 
f.close() 
p = re.compile('<w:tr.*?(?P<tag>{%.*?%}).*?</w:tr>', re.DOTALL) 
new_xml = p.sub('\g<tag>', orig_xml, 0) 
print new_xml 

實際結果這個正則表達式是:

<!-- regex_trial.xml --> 
<w:tbl> 
    {% for i in items %} 
    {% endfor %} 
</w:tbl> 

任何幫助,非常感謝!如果我們能夠弄清楚這一點,我們將能夠動態生成從Django驅動的站點上的MS Word docx文件。謝謝!!

更新:這是最後的代碼,我用

from xml.etree import ElementTree 
import cStringIO as StringIO 

TEMPLATE_TAG = 'template_text' 

tree = ElementTree.parse('regex_trial.xml') 
rows = tree.getiterator('tr') 
for row in rows: 
    for cell in row.getiterator('t'): 
     if cell.text and cell.text.find('{%') >= 0: 
      template_tag = cell.text 
      row.clear() 
      row.tag = TEMPLATE_TAG 
      row.text = template_tag 
      break 

output = StringIO.StringIO() 
tree.write(output) 
xml = output.getvalue() 
xml = xml.replace('<%s>' % TEMPLATE_TAG, '') 
xml = xml.replace('</%s>' % TEMPLATE_TAG, '') 
print xml 

感謝所有幫助!

+2

@ user425130您曾經考慮過使用XML/XSLT解析器? – 2011-05-04 03:19:43

+0

感謝您的支持!我考慮過XSL,但這很糟糕 - 您必須檢查每個tr,您需要有一個xpath表達式來檢查子級tc的內容。然後,你必須處理我們不想接觸的其他99%的內容。啊。 – aeb6 2011-05-04 04:03:50

+0

@ user425130,+1解決方案!Bravo – 2011-05-04 11:58:49

回答

4

,請不要使用正則表達式這個問題。

我是認真的,解析XML與正則表達式是很難的,它使你的代碼50X被其他人少維護。

lxml是pythonistas用於解析XML的實際工具...查看this article on Stack Overflow的示例用法。或者考慮this answer,其中應該是已被接受的答案。

我將其作爲快速演示進行了破解...它搜索的<w:tc>帶有非空的<w:t>兒童,並在每個元素的旁邊打印好。

import lxml.etree as ET 
from lxml.etree import XMLParser 

def worthy(elem): 
    for child in elem.iterchildren(): 
     if (child.tag == 't') and (child.text is not None): 
      return True 
    return False 

def dump(elem): 
    for child in elem.iterchildren(): 
     print "Good", child.tag, child.text 

parser = XMLParser(ns_clean=True, recover=True) 
etree = ET.parse('regex_trial.xml', parser) 
for thing in etree.findall("//"): 
    if thing.tag == 'tc' and worthy(thing): 
     dump(thing) 

產量...

Good t Header 1 
Good t Header 2 
Good t Header 3 
Good t {% for i in items %} 
Good t {{ i.field1 }} 
Good t {{ i.field2 }} 
Good t {{ i.field3 }} 
Good t {% endfor %} 
+0

+1:'xml.etree.ElementTree'也是一個合適的選擇,如果你想留在標準庫中。 – jathanism 2011-05-04 03:32:19

+0

@jathanism感謝您指出,我一直使用'lxml' – 2011-05-04 03:34:32

+0

任何關於如何用xml lib來做這件事的建議?覺得我需要檢查每個節點,看它是否包含任何包含「{%*%}」的節點,然後用子節點的內容替換。開始變得複雜。另外,我需要將文檔的剩餘部分(超過99%)保留下來,這就是我尋找/替換路線的原因。任何示例都會非常有幫助。謝謝! – aeb6 2011-05-04 04:10:59

3

永遠不要解析HTML或XML或SGML用正則表達式。

總是使用像lxml,libxml2或Beautiful這樣的工具 - 他們將永遠比你的代碼更聰明,更好的工作 工作。