正则表达式匹配(可能是多个)标签对之间的所有内容
Regex to match everything between (potentially multiple) pairs of tags
我知道以前有人问过这个问题,我知道正则表达式不太适合管理 XML。虽然,我在 python 实用程序中使用它们和 re.compile(...).subn(...)
来替换一些 XML 内容而不解析 XML 本身,因为这个 XML 内容在 proprietary/legacy 语言文件。因此,XML 工具不是一种选择,正则表达式是考虑编写特定算法之前的最后手段。
我需要替换元素中包含的某些内容(属性值)。
例如,来自:
<Tag>
bla bla
<SomethingElse AnAttribute="YEAH"/>
bla bla
</Tag>
收件人:
<Tag>
bla bla
<SomethingElse AnAttribute="AH,NO!!"/>
bla bla
</Tag>
为了执行匹配,我尝试了两种简单模式(使用非贪婪运算符):
<Tag>(.*?)AnAttribute="(.*?)"(.*?)</Tag>
还有非贪婪运算符 + 负先行的:
<Tag>(?!Tag)(.*?)CurrencyCode="(.*?)"(?!Tag)(.*?)</Tag>
它们都适用于简单的情况(最后一个更好地处理 "false positives"),但我仍然无法处理以下(非常简单!)情况:
<Tag></Tag>
bla bla
<SomethingElse AnAttribute="YEAH"/>
bla bla
<Tag></Tag>
因为在这种情况下 AnAttribute
是 实际上被发现的(并且 它不应该 因为它不在元素内部) !!
我认为您需要将问题分为两步:
- 提取所需标签内的相关文本部分
- 替换该标签内的属性
这可以通过使用 re.sub
并将闭包作为参数传递来完成。
以下使用 partial
将额外参数传递给闭包并动态构建所需的正则表达式:
import re
from functools import partial
text = u'''
[...]
'''
# The key is the external tag to extract
# The value a list of attributes whose content has to be replaced
sub_dict = {"RoomRatesWithoutServices": ['CurrencyCode1', 'CurrencyCode2'],
"AnotherTag": ['AnotherAttr']}
replacement = '_REPLACED_'
def closure(attr, replacement, m):
attr_pattern = '(?<=(?:%s)=")[^"]+(?=")' % attr
return re.sub(attr_pattern, replacement, m.group())
for ext_tag, attr_list in sub_dict.iteritems():
attr = r"|".join(attr_list)
tag_pattern = r"(?s)<%s>.*?</%s>" % (ext_tag, ext_tag)
text = re.sub(tag_pattern, partial(closure, attr, replacement), text)
print text
输出如下:
'<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&'
'<RoomRatesWithoutServices>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode1="_REPLACED_"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode2="_REPLACED_"/>&
<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&'
'<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&'
'<AnotherTag>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" AnotherAttr="_REPLACED_"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" AnotherAttr="_REPLACED_"/>&
<AnotherTag>&
</AnotherTag>&'
尝试在线 DEMO
我知道以前有人问过这个问题,我知道正则表达式不太适合管理 XML。虽然,我在 python 实用程序中使用它们和 re.compile(...).subn(...)
来替换一些 XML 内容而不解析 XML 本身,因为这个 XML 内容在 proprietary/legacy 语言文件。因此,XML 工具不是一种选择,正则表达式是考虑编写特定算法之前的最后手段。
我需要替换元素中包含的某些内容(属性值)。 例如,来自:
<Tag>
bla bla
<SomethingElse AnAttribute="YEAH"/>
bla bla
</Tag>
收件人:
<Tag>
bla bla
<SomethingElse AnAttribute="AH,NO!!"/>
bla bla
</Tag>
为了执行匹配,我尝试了两种简单模式(使用非贪婪运算符):
<Tag>(.*?)AnAttribute="(.*?)"(.*?)</Tag>
还有非贪婪运算符 + 负先行的:
<Tag>(?!Tag)(.*?)CurrencyCode="(.*?)"(?!Tag)(.*?)</Tag>
它们都适用于简单的情况(最后一个更好地处理 "false positives"),但我仍然无法处理以下(非常简单!)情况:
<Tag></Tag>
bla bla
<SomethingElse AnAttribute="YEAH"/>
bla bla
<Tag></Tag>
因为在这种情况下 AnAttribute
是 实际上被发现的(并且 它不应该 因为它不在元素内部) !!
我认为您需要将问题分为两步:
- 提取所需标签内的相关文本部分
- 替换该标签内的属性
这可以通过使用 re.sub
并将闭包作为参数传递来完成。
以下使用 partial
将额外参数传递给闭包并动态构建所需的正则表达式:
import re
from functools import partial
text = u'''
[...]
'''
# The key is the external tag to extract
# The value a list of attributes whose content has to be replaced
sub_dict = {"RoomRatesWithoutServices": ['CurrencyCode1', 'CurrencyCode2'],
"AnotherTag": ['AnotherAttr']}
replacement = '_REPLACED_'
def closure(attr, replacement, m):
attr_pattern = '(?<=(?:%s)=")[^"]+(?=")' % attr
return re.sub(attr_pattern, replacement, m.group())
for ext_tag, attr_list in sub_dict.iteritems():
attr = r"|".join(attr_list)
tag_pattern = r"(?s)<%s>.*?</%s>" % (ext_tag, ext_tag)
text = re.sub(tag_pattern, partial(closure, attr, replacement), text)
print text
输出如下:
'<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&'
'<RoomRatesWithoutServices>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode1="_REPLACED_"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode2="_REPLACED_"/>&
<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&'
'<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" CurrencyCode="EUR"/>&
<RoomRatesWithoutServices>&
</RoomRatesWithoutServices>&'
'<AnotherTag>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" AnotherAttr="_REPLACED_"/>&
<TotalBeforeTaxPayHotel AmountAfterTax="560.00" AnotherAttr="_REPLACED_"/>&
<AnotherTag>&
</AnotherTag>&'
尝试在线 DEMO