Python正则表达式中的多个负回顾断言?

19

我是编程新手,如果这个问题看起来很简单,请见谅:我有一段文本,我想使用正则表达式将其拆分为单个句子。我使用 .split 方法搜索一个点后跟一个大写字母,例如:

"\. A-Z"

然而我需要以以下方式细化这个规则:点.不能在AbsS之前,如果它后面是一个大写字母(A-Z),而且它是月份名称,如January | February | March,它也不应匹配。

我尝试实现前半部分,但即使如此也没有起作用。我的代码是:

"( (?<!Abs)\. A-Z) | (?<!S)\. A-Z) ) "

我建议将空格替换为\s+(或者如果确实需要精确一个空格,则为\s)。匹配大写字母应该是[A-Z](你忘了括号)。 - hochl
5个回答

23
首先,我认为您可能希望将空格替换为\s+\s(如果确实只有一个空格,您可以在英文文本中经常发现双倍空格)。
其次,要匹配大写字母,您必须使用[A-Z],但是A-Z不起作用(但请记住,除了A-Z还可能有其他大写字母...)。
此外,我认为我知道为什么这行不通。正则表达式引擎将尝试匹配\.[A-Z],如果它没有被AbsS所前缀。问题是,如果它由S前缀,则它不是由Abs前缀,因此第一种模式匹配。如果由Abs前缀,则它不是由S前缀,因此第二个模式版本匹配。无论如何,这两种模式之一都会匹配,因为AbsS是互斥的。
您提出的问题的模式可能是:
(?<!Abs)(?<!S)(\. [A-Z])
或者
(?<!Abs)(?<!S)(\.\s+[A-Z])

这是因为你需要避免使用|,如果没有它,表达式现在表示 不是以Abs为前缀不是以S为前缀。如果两者都为真,则模式匹配器将继续扫描字符串并找到匹配项。

为了排除月份名称,我提出了这个正则表达式:

(?<!Abs)(?<!S)(\.\s+)(?!January|February|March)[A-Z]

对于否定先行断言模式,同样适用相同的论点。


2
我对多个向后断言还不熟悉。看起来 (?<!Abs)(?<!S) 和 (?<!Abs|S) 的作用是相同的。除了个人偏好(简洁性/可读性)之外,它们是否有任何优势? - jhiro009
2
@jhiro009 是的,当您使用OR(管道)运算符将它们组合在一起时,正则表达式要求它们是固定宽度的模式,因此Abs和S是不兼容的。在这种情况下,您必须使用前一种情况。 - Joel Wigton

9
我在标题中添加了一个简短的答案,因为这是谷歌搜索结果的顶部:
有多个不同长度的负回溯的方法是将它们像这样链接在一起:
"(?<!1)(?<!12)(?<!123)example"
这将匹配 example, 2example3example,但不会匹配 1example, 12example123example

1
使用nltk punkt tokenizer。它比使用正则表达式更加健壮。
>>> import nltk.data
>>> text = """
... Punkt knows that the periods in Mr. Smith and Johann S. Bach
... do not mark sentence boundaries.  And sometimes sentences
... can start with non-capitalized words.  i is a good variable
... name.
... """
>>> sent_detector = nltk.data.load('tokenizers/punkt/english.pickle')
>>> print '\n-----\n'.join(sent_detector.tokenize(text.strip()))
Punkt knows that the periods in Mr. Smith and Johann S. Bach
do not mark sentence boundaries.
-----
And sometimes sentences
can start with non-capitalized words.
-----
i is a good variable
name.

1
使用nltk或者类似@root建议的工具。
回答正则表达式问题:
import re
import sys

print re.split(r"(?<!Abs)(?<!S)\.\s+(?!January|February|March)(?=[A-Z])",
               sys.stdin.read())

输入

First. Second. January. Third. Abs. Forth. S. Fifth.
S. Sixth. ABs. Eighth

输出

['First', 'Second. January', 'Third', 'Abs. Forth', 'S. Fifth',
 'S. Sixth', 'ABs', 'Eighth']

-2

您可以使用 Set []。

'(?<![1,2,3]example)'

这将不匹配 1example、2example、3example。


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