在regex中查找具有相同字符串的两个匹配项

时间:2017-12-08 15:03:26

标签: python regex python-3.x

如果在给定结构中出现两次字符串(例如在XML解析中),是否有一种方法可以找到字符串? 这段代码显然不起作用,因为它找到第一个标签,然后是最后一个结束标签:

re.findall(r'<(.+)>([\s\S]*)</(.+)>', s)

那么有没有办法告诉正则表达式第三个匹配应该与第一个匹配?

完整代码:

import re

s = '''<a1>
    <a2>
        1
    </a2>
    <b2>
        52
    </b2>
    <c2>
        <a3>
            Abc
        </a3>
    </c2>
</a1>
<b1>
    21
</b1>'''

matches = re.findall(r'<(.+)>([\s\S]*)</(.+)>', s)
for match in matches:
    print(match)

结果应该是包含内容的所有标签:

    [('a1', '\n    <a2>\n        1\n    </a2>\n    <b2>\n        52\n    </b2>\n    <c2>\n        <a3>\n            Abc\n        </a3>\n    </c2>\n'),
     ('a2', '\n        1\n    '),
      ...]

注意:我不是在寻找一个完整的xml解析包。问题特别是关于用正则表达式解决给定的问题。

3 个答案:

答案 0 :(得分:4)

您可以使用backreferences和简单的递归:

>>> def m(s):
...    matches = re.findall(r'<(.+)>([\s\S]*)</(\1)>', s)
...    for k,s2,_ in matches:
...        print (k,s2)
...        m(s2)
... 
>>> m(s)
('a1', '\n    <a2>\n  ...[dropped]...      </a3>\n    </c2>\n')
('a2', '\n        1\n    ')
('b2', '\n        52\n    ')
('c2', '\n        <a3>\n            Abc\n        </a3>\n    ')
('a3', '\n            Abc\n        ')
('b1', '\n    21\n')

有关backreferences from Microsoft Docs的更多信息。

<强>被修改

为了额外的乐趣,使用发电机。感谢@mrCarnivore关于您删除if s的建议:

>>> def m(s):
...    matches = re.findall(r'<(.+)>([\s\S]*)</(\1)>', s)
...    for k,s2,_ in matches:
...        yield (k,s2)
...        yield from m(s2)
... 
>>> for x in m(s):
...    x
... 
('a1', '\n    <a2>\ [....]     Abc\n        </a3>\n    </c2>\n')
('a2', '\n        1\n    ')
('b2', '\n        52\n    ')
('c2', '\n        <a3>\n            Abc\n        </a3>\n    ')
('a3', '\n            Abc\n        ')
('b1', '\n    21\n')

答案 1 :(得分:1)

我不会这样做,因为递归结构很难用正则表达式解析。 Python的re模块不支持此功能。替代regex模块可以。但是,我不会这样做。

backreference只能为您带来这么远:

import re

s = '''<a1>
    <a2>
        1
    </a2>
    <b2>
        52
    </b2>
    <c2>
        <a3>
            Abc
        </a3>
    </c2>
</a1>
<b1>
    21
</b1>'''

matches = re.findall(r'<(.+)>([\s\S]*)</\1>', s) # mind the \1
for match in matches:
    print(match)

它会为您提供两场比赛:1。<a1> ... </a1><b1> ... </b1>

现在想象一下你的一些标签都有属性。如果标签可以跨越多行怎么办?关闭自己的标签怎么样?意外空间呢?

html / xml解析器可以处理所有这些。

答案 2 :(得分:0)

使用帮助danihp给了我答案并遵守评论中的提示DDeMartini我能够创建一个轻量级xml解析器,它返回xml的dict结构:

import re

def xml_loads(xml_text):
    matches = re.findall(r'<([^<>]+)>([\s\S]*)</(\1)>', xml_text)
    if not matches:
        return xml_text.strip()
    d = {}
    for k, s2, _ in matches:
        d[k] = xml_loads(s2)
    return d


s = '''<a1>
    <a2>
        1
    </a2>
    <b2>
        52
    </b2>
    <c2>
        <a3>
            Abc
        </a3>
    </c2>
</a1>
<b1>
    21
</b1>'''

d = xml_loads(s)
print(d)