未有名 发表于 2023-7-12 12:31:00

python re 正则匹配问题

现有xml文档如下:
<root>
<a>
    <b>字0</b>
</a>
<a>
    <b>字1</b>
</a>
<a>
    <b>字2</b>
</a>
</root>

现在我需要用re正则表达式匹配:
<a>
<b>字1</b>
</a>

无论我如何尝试,都只能匹配到下面的情况:

<a>
<b>字0</b>
</a>
<a>
<b>字1</b>
</a>

使用的re pattern为"<a[\s\S]*?字1[\s\S]*?a>"

请问能有什么方法?

陶远航 发表于 2023-7-12 12:31:11

使用正则表达式来解析 XML 是一个常见的问题。然而,由于 XML 是一种结构化的数据格式,正则表达式并不是最适合解析它的工具。推荐使用专门处理 XML 的库,如 xml.etree.ElementTree 或 lxml。

以下是使用 xml.etree.ElementTree 模块解析 XML 并找到目标节点的示例代码:

import xml.etree.ElementTree as ET

# 定义 XML 数据
data = "<root><a><b>字0</b></a><a><b>字1</b></a><a><b>字2</b></a></root>"

# 解析 XML
root = ET.fromstring(data)

# 使用 XPath 表达式查找目标节点
target_node = root.find(".//a")

# 输出结果
if target_node is not None:
    print(ET.tostring(target_node, encoding="unicode"))
else:
    print("未找到匹配的节点")


在上述代码中,我们使用 ET.fromstring() 函数将 XML 字符串转换为 Element 对象。然后,使用 find() 方法和 XPath 表达式 .//a 找到匹配的目标节点。最后,使用 ET.tostring() 函数将节点转换回字符串并打印输出。

请注意,XPath 表达式 .//a 表示在整个 XML 树中查找包含 <b> 元素且其文本内容为 "字1" 的 <a> 元素。

使用专门的 XML 解析库可以更方便、可靠地处理复杂的 XML 结构,而不需要自己编写复杂的正则表达式。这样可以更好地保持代码的可读性和可维护性。

歌者文明清理员 发表于 2023-7-12 12:33:32

你要匹配啥?
还有<a[\s\S]*?字1[\s\S]*?a>,里面不是有“字1”了吗,python会一直往后匹配

isdkz 发表于 2023-7-12 12:38:06

在 Python 中,对于解析 XML 文档,通常推荐使用专门的 XML 解析库,例如xml.etree.ElementTree或lxml ,而不是使用正则表达式。这是因为 XML 文档的结构可能非常复杂,并且使用正则表达式可能会导致很多意外的匹配。

然而,如果你还是想继续使用正则表达式,可以试试以下这个正则表达式:

re.findall(r"<a>\s*<b>字1</b>\s*</a>", your_xml_string)


这个正则表达式的核心思想是利用\s*来匹配任意数量的空白字符(包括空格,制表符,换行符等)。这样可以确保即使<b>标签前后有空格或者换行,也能正确地匹配。

但是我还是强烈建议你使用 XML 解析库来处理这类问题,这样更为稳定和有效。下面是一个使用xml.etree.ElementTree的例子:

import xml.etree.ElementTree as ET

data = """
<root>
<a>
    <b>字0</b>
</a>
<a>
    <b>字1</b>
</a>
<a>
    <b>字2</b>
</a>
</root>
"""

root = ET.fromstring(data)

for a in root.findall('a'):
    b = a.find('b')
    if b is not None and b.text == '字1':
      print(ET.tostring(a).decode())


这段代码首先解析 XML 数据,然后查找所有的<a>元素,对于每一个<a>元素,再查找其下的<b>元素,并检查其内容是否为 '字1'。如果是,就打印出这个<a>元素。这种方法的优点是能够正确地处理复杂的 XML 数据,并且更加灵活和可控。

未有名 发表于 2023-7-12 12:41:25

歌者文明清理员 发表于 2023-7-12 12:33
你要匹配啥?
还有,里面不是有“字1”了吗,python会一直往后匹配

我只需要包含"字1"的a标签,但是匹配结果总是给了不止一个a标签,
我自己是知道原因的:
    匹配从头部开始匹配<a
    然后往后匹配[\s\S]*?即任何字符
    直到碰见特殊设定的字符字1
    然后继续往后匹配[\s\S]*?
    直到最后匹配到a>的关闭标签
问题就出在第一步
匹配开始标签时的位置就错了,导致第二步时直接匹配了一个完整的 a 标签在里面
我不知道该怎么解决

歌者文明清理员 发表于 2023-7-12 12:44:59

未有名 发表于 2023-7-12 12:41
我只需要包含"字1"的a标签,但是匹配结果总是给了不止一个a标签,
我自己是知道原因的:
    匹配从头部 ...

你可以尝试使用非贪婪匹配模式,将 <a> 和 </a> 之间的内容匹配为一个分组,并在该分组中继续匹配 <b>字1</b>。以下是修改后的正则表达式示例:

<a[\s\S]*?<b>字1<\/b>[\s\S]*?<\/a>

这样修改后的正则表达式会选择最短的匹配,确保只匹配到 <a> 和 <b>字1</b> 在同一个 <a> 标签内的情况。

未有名 发表于 2023-7-12 12:50:34

歌者文明清理员 发表于 2023-7-12 12:44
你可以尝试使用非贪婪匹配模式,将和之间的内容匹配为一个分组,并在该分组中继续匹配 字1。以下是修 ...

没有用的
匹配会从包含"字0"的 a 标签头开始
直到匹配到"字1",然后最后再去匹配 a 的关闭标签
在<a 和 字1 之间的匹配非贪婪也没用
就是没办法跳过包含"字0"的 a 标签

歌者文明清理员 发表于 2023-7-12 12:52:45

本帖最后由 歌者文明清理员 于 2023-7-12 12:53 编辑

未有名 发表于 2023-7-12 12:50
没有用的
匹配会从包含"字0"的 a 标签头开始
直到匹配到"字1",然后最后再去匹配 a 的关闭标签


import re
xml='''<root>
<a>
    <b>字0</b>
</a>
<a>
    <b>字1</b>
</a>
<a>
    <b>字2</b>
</a>
</root>'''
match = re.match("<a.*?</a>.*?(<a.*?字1.*?</a>)", xml)
print(match.group(1))

歌者文明清理员 发表于 2023-7-12 12:58:25

这种不要用正则了,用bs4(非gpt):
from bs4 import BeautifulSoup
xml = '''...'''
soup = BeautifulSoup(xml, "lxml")
tags = soup.find_all("a")
target = None
for tag in tags:
    if "字1" in str(tag):
      target = tag
      break
print(str(target))

歌者文明清理员 发表于 2023-7-12 13:13:23

还有一种
你可以尝试使用非贪婪模式匹配的方式来解决这个问题。在正则表达式中,非贪婪模式通过在匹配符号后面添加一个问号(?)来实现。

在你的例子中,你可以将正则表达式修改为"<a[\s\S]*?字1[\s\S]*?</a>"。这样修改后,正则表达式会匹配最短的满足条件的字符串,即你所期望的结果。

下面是修改后的代码示例:

```python
import re

xml = '''
<root>
<a>
    <b>字0</b>
</a>
<a>
    <b>字1</b>
</a>
<a>
    <b>字2</b>
</a>
</root>
'''

pattern = r"<a[\s\S]*?字1[\s\S]*?</a>"
result = re.findall(pattern, xml)
print(result)
```

输出结果为:

['<a>\n    <b>字1</b>\n</a>']

这样你就可以得到你所需的匹配结果了。

未有名 发表于 2023-7-12 13:27:46

歌者文明清理员 发表于 2023-7-12 12:52


其实我的真实情况是,我不能使用python
只有使用notepad--自带的正则表达式去实现匹配替换
谢谢你的回答

歌者文明清理员 发表于 2023-7-12 13:31:00

未有名 发表于 2023-7-12 13:27
其实我的真实情况是,我不能使用python
只有使用notepad--自带的正则表达式去实现匹配替换
谢谢你的回 ...

那你就去匹配<a[\s\S]*?字1[\s\S]*?</a>

未有名 发表于 2023-7-12 13:36:05

歌者文明清理员 发表于 2023-7-12 13:31
那你就去匹配

这个答案不对

未有名 发表于 2023-7-12 13:36:45

歌者文明清理员 发表于 2023-7-12 13:13
还有一种
你可以尝试使用非贪婪模式匹配的方式来解决这个问题。在正则表达式中,非贪婪模式通过在匹配符号 ...

你这个给的python代码跑出来的结果是
['<a>\n    <b>字0</b>\n</a>\n<a>\n    <b>字1</b>\n</a>']

歌者文明清理员 发表于 2023-7-12 13:43:47

未有名 发表于 2023-7-12 13:36
你这个给的python代码跑出来的结果是
['\n    字0\n\n\n    字1\n']

属实做不到
页: [1]
查看完整版本: python re 正则匹配问题