在正则弦中查找与相同字符串的两个匹配



如果以色双音符在给定的结构中发生两次(即,就像在XML解析中一样),是否有办法找到字符串?显然,此代码无效,因为它找到了第一个标签,然后是最后一个关闭标签:

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

因此,有没有办法告诉Regex,第三次匹配应与第一个比赛相同?

完整代码:

import re
s = '''<a1>
    <a2>
        1
    </a2>
    <b2>
        52
    </b2>
    <c2>
        <a3>
            Abc
        </a3>
    </c2>
</a1>
<b1>
    21
</b1>'''
matches = re.findall(r'<(.+)>([sS]*)</(.+)>', s)
for match in matches:
    print(match)

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

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

注意:我不是在寻找完整的XML解析包。问题特定是关于解决给定问题的问题。

您可以使用backeference和简单递归:

>>> def m(s):
...    matches = re.findall(r'<(.+)>([sS]*)</(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        1n    ')
('b2', 'n        52n    ')
('c2', 'n        <a3>n            Abcn        </a3>n    ')
('a3', 'n            Abcn        ')
('b1', 'n    21n')

更多有关Microsoft文档的反向表示的信息。

编辑

为发电机提供额外的乐趣。感谢@MrCarnivore您的建议删除if s

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

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

反向注册只能带给您这么远:

import re
s = '''<a1>
    <a2>
        1
    </a2>
    <b2>
        52
    </b2>
    <c2>
        <a3>
            Abc
        </a3>
    </c2>
</a1>
<b1>
    21
</b1>'''
matches = re.findall(r'<(.+)>([sS]*)</1>', s) # mind the 1
for match in matches:
    print(match)

它将为您提供两个匹配:1。<a1> ... </a1><b1> ... </b1>

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

HTML/XML解析器可以处理所有这些。

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

import re
def xml_loads(xml_text):
    matches = re.findall(r'<([^<>]+)>([sS]*)</(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)

最新更新