Python 正则表达式获得唯一的多行匹配

Python Regex get Unique Multiline Matches

由于背景解释起来很复杂,我正在编写伪代码, 我只对 Python-Regex-Pattern 感兴趣,希望你们中的一个能帮助我

我有以下输入文本(很多行 \n 作为行分隔符压缩为 '.'):

.
.
1 Order 
order1 stuff
order1 stuff
etc
ShippingMethod: Truck
.
.
2 Order
order2 stuff
order2 stuff
etc
ShippingMethod: Truck
.
.
Order Summary
.
.

我只想为每个订单单独匹配 'Order' 和 'Truck' 之间的文本 ,然后我会进一步迭代结果程序。

我的正则表达式:(为了更好的可读性,我分为“开始、内容、结束”)。

pattern = \d\s*Order + [.|\s|\S]* + Truck

当我执行这场比赛时,我得到一个结果,从 1 Order 开始到 Truck:

1 Order 
order1 stuff
order1 stuff
etc
ShippingMethod: Truck
.
.
2 Order
order2 stuff
order2 stuff
etc
ShippingMethod: Truck

我想要(在这种情况下)恰好两个 只包含一个订单内容的匹配项:

1 Order 
order1 stuff
order1 stuff
etc
ShippingMethod: Truck
2 Order
order2 stuff
order2 stuff
etc
ShippingMethod: Truck

希望您清楚我在寻找什么。非常感谢任何帮助。
提前致谢,注意安全,保持健康!

您可能会建议的事情:

解决方案看似简单 - 使用非贪婪运算符 ?

首先,字符 class 正则表达式 [] 匹配其中的任何字符,因此要匹配 ab 正则表达式是 [ab]而不是 [a|b]。所以你代码的 content 部分应该是 [.\s\S].
此外,\s\S 匹配所有 space s 和非 spaces,因此句点 (.) 与此处无关。

所以最后的内容部分应该是这样的:[\s\S]*

现在是实际解决方案:

贪婪的 ? 运算符在任何正常频率运算符(如 +*? 之后告诉正则表达式匹配 作为少数element/s尽可能。使用 *,您使用的是 零或更多 的默认贪婪版本,告诉正则表达式匹配尽可能多的(最终甚至匹配第一个 Truck你想要!)

所以我们在末尾添加了一个非贪婪运算符,因此最终的正则表达式如下所示:

\d\s*Order[\s\S]*?Truck

奖金建议:

字符 class [\s\S] 是告诉正则表达式匹配每个字符的巧妙方法(因为每个字符要么是 space 要么不是 space) .但事实证明,有一种方法可以通过使用 re.DOTALL 修饰符来提高效率。它按照它说的做 - 它告诉正则表达式 .(DOT)应该匹配所有字符,包括换行符。

如果这是您使用的代码:

re.findall(r'\d\s*Order[\s\S]*?Truck', input_text)

这是最好的代码(包括问题的解决方案):

re.findall(r'\d\s*Order.*?Truck', input_text, re.DOTALL)

如您所见,.*? 现在将匹配从 OrderTruck 的所有内容(包括换行符)。

不使用re.DOTALL,如果Truck不存在,防止过度匹配,您可以使用:

^\d+\s*Order\b.*(?:\n(?!\d+\s* Order\b|.* Truck$).*)*\n.* Truck$

模式匹配:

  • ^ 字符串开头
  • \d+\s*Order\b.* 匹配数字后跟 Order 和行的其余部分
  • (?:非捕获组
    • \n(?!\d+\s* Order\b|.* Truck$) 匹配换行符并断言该行不以数字开头和 Order 并断言该行不以 Truck
    • 结尾
    • .* 如果断言为真,匹配整行
  • )*关闭非捕获组以匹配所有行
  • \n.* Truck$ 匹配换行符和以 Truck
  • 结尾的行的其余部分

Regex demo | Python demo

import re
 
regex = r"^\d+\s*Order\b.*(?:\n(?!\d+\s* Order\b|.* Truck$).*)*\n.* Truck$"
 
s = ("\n\n"
    "1 Order \n"
    "order1 stuff\n"
    "order1 stuff\n"
    "etc\n"
    "ShippingMethod: Truck\n\n\n"
    "2 Order\n"
    "order2 stuff\n"
    "order2 stuff\n"
    "etc\n"
    "ShippingMethod: Truck\n\n\n"
    "Order Summary\n\n")
 
print(re.findall(regex, s, re.MULTILINE))

输出

['1 Order \norder1 stuff\norder1 stuff\netc\nShippingMethod: Truck', '2 Order\norder2 stuff\norder2 stuff\netc\nShippingMethod: Truck']