正则表达式匹配(可能是多个)标签对之间的所有内容

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 实际上被发现的(并且 它不应该 因为它不在元素内部) !!

我认为您需要将问题分为两步:

  1. 提取所需标签内的相关文本部分
  2. 替换该标签内的属性

这可以通过使用 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