【问题标题】:non-greedy behavior with multiline and dotallmultiline 和 dotall 的非贪婪行为
【发布时间】:2025-12-11 18:55:01
【问题描述】:

我有以下字符串,我尝试将对应的“foo”中的字符串匹配到“bar=1”、“bar=2”或“bar=3”。所以一次只有一场比赛。

file_header

foo lorem ipsum \pope
24 dolor sit amet, consectetur adipisici elit
Excepteur sint obcaecat cupiditat non 
gnu blu bar=1

foo lorem ipsum \
@>@!@ consectetur adipisici elit
gnu blu bar=2
foo lorem ipsum
23 dolor sit amet, consectetur adipisici elit
gnu blu bar=3

foo ... etc

我尝试了所有方法,从简单的^foo.*?bar=2$ 到类似^(?!\bfoo\b.*\bfoo\b).*\bfoo\b.*bar=2$。但是激活多行和dotall 后,它将始终匹配第一个'foo',甚至标记整个文件头。 :(

在使用 multiline 和 dotall 时,似乎不可能进行非贪婪行为。

【问题讨论】:

  • 那么,寻找类似^foo(?:(?!^foo|bar=2$).)*bar=2$的东西?
  • 1) 您是在尝试匹配还是搜索? 2) 请提供一个简短、完整的程序来证明您的错误。说“我什么都试过了”并没有你想象的那么有用。相反,请在一个完整程序中向我们展示您尝试过的一件特别的事情。请参阅minimal reproducible example,了解有关提出能够产生出色答案的问题的更多信息。
  • @Robᵩ 我只是在 pythex.org 上进行试验,还没有任何 python 代码。这就是为什么我不知道匹配和搜索之间有区别。 (我需要搜索)

标签: python regex


【解决方案1】:

您可以使用经过调和的贪婪令牌,例如

^foo(?:(?!^foo|bar=2$).)*bar=2$

(?:(?!^foo|bar=2$).)* 匹配任何不是foo(位于行/字符串的开头)且不是位于行/字符串末尾的bar=2 的文本。

请参阅regex demo。然而,这样的构造是消耗资源的,建议展开它。这是一个选项:

^foo[^\nb]*(?:\n(?!foo)[^\nb]*|b(?!ar$)[^\nb]*)*bar=2$

another demo

【讨论】:

  • 维克托,你是个野兽!谢谢!
  • 不客气。 Lazy 并不总是返回最短的匹配。
【解决方案2】:

此程序查找所有不重叠的 foo <stuff> bar=<number>。注意re.MULTILINE-mode 中非贪婪运算符的成功使用,使用这个表达式:^foo.*?bar=\d+$

import re
from pprint import pprint

data = '''
file_header

foo lorem ipsum \pope
24 dolor sit amet, consectetur adipisici elit
Excepteur sint obcaecat cupiditat non 
gnu blu bar=1

foo lorem ipsum \
@>@!@ consectetur adipisici elit
gnu blu bar=2
foo lorem ipsum
23 dolor sit amet, consectetur adipisici elit
gnu blu bar=3
'''

matches = re.findall(r'^foo.*?bar=\d+$', data, re.DOTALL|re.MULTILINE)
pprint (matches)

结果:

['foo lorem ipsum \\pope\n24 dolor sit amet, consectetur adipisici elit\nExcepteur sint obcaecat cupiditat non \ngnu blu bar=1',
 'foo lorem ipsum @>@!@ consectetur adipisici elit\ngnu blu bar=2',
 'foo lorem ipsum\n23 dolor sit amet, consectetur adipisici elit\ngnu blu bar=3']

【讨论】:

  • 请注意,这是有效的,因为数据组织得很好,并且匹配 foo 到 bar。如果可以嵌套 foo/bars,或者即使 bar 是一个特定的集合,即 [234],这将无法让内部 foo 变为 bar。
  • 似乎这只有效,因为re.findall(...)“返回所有非重叠匹配”,所以它很消耗。与re.search(r'^foo.*?bar=3+$', ...print matches.group(0) 相同的代码返回从第一个foo 到最后一个bar 的字符串。不幸的是,这不是我需要的。下次我会提供一个更好的例子来说明我需要什么,因为我的英语不是最好的。 :D
【解决方案3】:

贪婪从左到右起作用

MULTILINEDOTALL无关,是那个非贪心操作符 只影响比赛的结束,不影响比赛的开始。

要满足您的需求,请在您的图案前添加.*

>>> re.findall(r'.*(foo.*?bar=1)', s, re.DOTALL)
['foo lorem ipsum \\pope\n24 dolor sit amet, consectetur adipisici elit\nExcepteur sint obcaecat cupiditat non \ngnu blu bar=1']
>>> re.findall(r'.*(foo.*?bar=2)', s, re.DOTALL)
['foo lorem ipsum @>@!@ consectetur adipisici elit\ngnu blu bar=2']
>>> re.findall(r'.*(foo.*?bar=3)', s, re.DOTALL)
['foo lorem ipsum\n23 dolor sit amet, consectetur adipisici elit\ngnu blu bar=3']

【讨论】: