Python正则表达式中替代变宽度回顾后断言的方法

5

最近我决定踏入Python的深渊,并开始将我的一些R代码转换成Python。然而,我遇到了一个对我来说非常重要的问题。在我的工作中,我花费了大量时间解析文本数据,这种数据通常结构不明显。因此,我开始依赖正则表达式的前后查找功能,而且R的前后查找功能相当强大。例如,如果我正在解析一份PDF文档,可能会在字母之间添加一些空格,我需要使用类似以下的代码才能得到我想要的值:

oAcctNum <- str_extract(textBlock[indexVal], "(?<=ORIG\\s?:\\s?/\\s?)[A-Z0-9]+")

在Python中,这是不可能的,因为使用?使得回顾后发变成了可变宽度表达式而不是固定宽度表达式。这个功能对我来说非常重要,它阻止我想使用Python,但是我不想放弃这门语言,我想知道解决这个问题的Pythonista方式。我需要在提取文本之前预处理字符串吗?类似于这样:
oAcctNum = re.sub(r"(?<=\b\w)\s(?=\w\b)", "")
oAcctNum = re.search(r"(?<=ORIG:/)([A-Z0-9])", textBlock[indexVal]).group(1)

有没有更有效率的方法?因为虽然这个例子很简单,但我处理的数据经常以非常复杂的方式涉及到这个问题,如果我必须为每一行要分析的文本做这种预处理,那将非常麻烦。

最后,如果这不是正确的提问地点,我很抱歉;我不确定还有哪里可以发布。提前感谢。


2
regex 模块支持可变宽度的向后查找。另请参阅 https://dev59.com/1Wgu5IYBdhLWcg3wBym1 - jonrsharpe
@jonrsharpe 谢谢您的回复,这对我很有帮助!不过,看了下面的答案,我开始重新考虑对前后环视的依赖。但是,再次感谢您指向 regex 模块。 - tblznbits
3个回答

3
请注意,如果您可以使用分组,则通常不需要使用后顾断言。那么这个怎么样呢?
match = re.search(r"ORIG\s?:\s?/\s?([A-Z0-9]+)", string)
if match:
    text = match.group(1)

实际操作中:
>>> string = 'ORIG : / AB123'
>>> match = re.search(r"ORIG\s?:\s?/\s?([A-Z0-9]+)", string)
>>> match
<_sre.SRE_Match object; span=(0, 12), match='ORIG : / AB123'>
>>> match.group(1)
'AB123'

谢谢回复,Antii!你和stribizhev有相同的想法,看起来这是最佳实践。是时候重写一些代码了... - tblznbits

2
您需要在这种情况下使用捕获组,如您所描述的:

(您可以点击此处了解更多关于捕获组的信息)

"(?<=ORIG\\s?:\\s?/\\s?)[A-Z0-9]+"

将变为

r"ORIG\s?:\s?/\s?([A-Z0-9]+)"

该值将在.group(1)中。请注意,优先使用原始字符串。

以下是示例代码:

import re
p = re.compile(r'ORIG\s?:\s?/\s?([A-Z0-9]+)', re.IGNORECASE)
test_str = "ORIG:/texthere"
print re.search(p, test_str).group(1)

IDEONE演示

除非您需要重叠匹配,否则使用捕获组而不是后顾断言相当简单。


你提出了一个非常好的观点。看起来现在是重新思考我的正则表达式方法的时候了。 - tblznbits
重点在于:如果您不需要重叠匹配,请使用捕获组方法。回顾后查找会消耗资源,从性能上来说代价高昂。有时候,差别并不大,但如果回顾后查找很长,差异可能是明显的。 - Wiktor Stribiżew

1
print re.findall(r"ORIG\s?:\s?/\s?([A-Z0-9]+)",test_str)

你可以直接使用findall,如果正则表达式中存在分组,它将返回所有的分组。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接