如果在给定结构中出现两次字符串(例如在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解析包。问题特别是关于用正则表达式解决给定的问题。
答案 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)