问题描述
由于背景解释起来很复杂,我正在编写伪代码, 我只对 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”的原因。我会设法改写。
解决方法
解决方案看似简单 - 使用非贪婪运算符 ?
。
首先,字符类正则表达式 []
匹配其中的任何字符,因此要匹配 a
和 b
,正则表达式是 [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
结尾的行的其余部分
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']