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

问题描述

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

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

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

我只想匹配“订单”和“卡车”之间的文本对于每个订单,然后我将在程序中进一步迭代结果。

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

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

当我执行这场比赛时,我得到一个结果,从 1 Order 开始并在 second 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

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

您可能建议的事项:

  • 您必须在行首和单词之间假设不同数量的空格,因为输入文本是 PDF 文本提取器的结果。但是 \n 是可以信任的。基本上是写 \n 写 \s*\n
  • 我不能使用“订单”作为模式的结尾部分,因为在最后一个订单之后,下一个摘要
  • “ShippingMethod”在我的语言中有所不同,这就是我在此示例中使用“Truck”的原因。我会设法改写。

解决方法

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

首先,字符类正则表达式 [] 匹配其中的任何字符,因此要匹配 ab,正则表达式是 [ab] 而不是 {{1} }.所以代码的 content 部分应该是 [a|b]
另外,[.\s\S]\s 分别匹配所有空格和非空格,所以句号 (\S) 在这里无关紧要。

所以最终的内容部分应该是这样的:.

现在是实际的解决方案:

在任何正常频率运算符(如 [\s\S]*?+)之后的贪婪 * 运算符告诉正则表达式匹配 尽可能少的元素可能。使用 ?,您将使用 零个或多个 的默认贪婪版本,告诉正则表达式匹配尽可能多的(最终匹配第一个 {{1} } 你想要!)

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

*

额外建议:

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

如果这是您使用的代码:

re.DOTALL

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

.

如您所见,re.findall(r'\d\s*Order[\s\S]*?Truck',input_text) 现在将匹配从 re.findall(r'\d\s*Order.*?Truck',input_text,re.DOTALL) .*? 的所有内容(包括换行符)。

,

不使用 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']