在Python中提取段落中句子的正则表达式
我正在尝试用Python中的正则表达式从一段文字中提取一句话。
通常我测试的代码能正确提取句子,但在下面这段文字中,句子没有被正确提取出来。
这段文字是:
"但在疟疾感染和脓毒症的情况下,树突状细胞在全身集中精力警告免疫系统,这使得它们无法检测和应对任何新的感染。" 一种新型疫苗?
代码如下:
def splitParagraphIntoSentences(paragraph):
import re
sentenceEnders = re.compile('[.!?][\s]{1,2}(?=[A-Z])')
sentenceList = sentenceEnders.split(paragraph)
return sentenceList
if __name__ == '__main__':
f = open("bs.txt", 'r')
text = f.read()
mylist = []
sentences = splitParagraphIntoSentences(text)
for s in sentences:
mylist.append(s.strip())
for i in mylist:
print i
在用上面的段落测试时,输出结果和输入的段落完全一样,但输出应该是这样的:
但在疟疾感染和脓毒症的情况下,树突状细胞在全身集中精力警告免疫系统,这使得它们无法检测和应对任何新的感染
一种新型疫苗
这个正则表达式有什么问题吗?
3 个回答
是的,这里确实有问题。你只在分隔符后面跟着一个或两个空格,然后再跟一个大写字母时才考虑这个分隔符,所以像“A new type of vaccine?”这样的句子结尾就不会被匹配到。
我觉得对于空格的处理也不需要太严格,除非这是你的意图(因为文本可能格式不太好),比如“Hello Lucky Boy!How are you today?”就不会被分开。
我也不太明白你的例子,为什么只有第一个句子用引号括起来?
不管怎样:
>>> Text="""But in the case of malaria infections, dendritic cells and stuff.
A new type of vaccine? My uncle!
"""
>>> Sentences = re.split('[?!.][\s]*',Text)
>>> Sentences
['But in the case of malaria infections, dendritic cells and stuff',
'A new type of vaccine',
'My uncle',
'']
你可能还需要过滤掉空句子:
>>> NonemptyS = [ s for s in Senteces if s ]
Riccardo Murri的回答是对的,不过我想再多说一点。
之前有个类似的问题是关于PHP的:php句子边界检测。我对那个问题的回答中提到了如何处理一些特殊情况,比如“Mr.”、“Mrs.”和“Jr.”。我把那个正则表达式调整了一下,让它可以在Python中使用(因为Python对某些查找方式有更多限制)。下面是一个修改过并经过测试的脚本,使用了这个新的正则表达式:
def splitParagraphIntoSentences(paragraph):
import re
sentenceEnders = re.compile(r"""
# Split sentences on whitespace between them.
(?: # Group for two positive lookbehinds.
(?<=[.!?]) # Either an end of sentence punct,
| (?<=[.!?]['"]) # or end of sentence punct and quote.
) # End group of two positive lookbehinds.
(?<! Mr\. ) # Don't end sentence on "Mr."
(?<! Mrs\. ) # Don't end sentence on "Mrs."
(?<! Jr\. ) # Don't end sentence on "Jr."
(?<! Dr\. ) # Don't end sentence on "Dr."
(?<! Prof\. ) # Don't end sentence on "Prof."
(?<! Sr\. ) # Don't end sentence on "Sr."
\s+ # Split on whitespace between sentences.
""",
re.IGNORECASE | re.VERBOSE)
sentenceList = sentenceEnders.split(paragraph)
return sentenceList
if __name__ == '__main__':
f = open("bs.txt", 'r')
text = f.read()
mylist = []
sentences = splitParagraphIntoSentences(text)
for s in sentences:
mylist.append(s.strip())
for i in mylist:
print i
你可以看到它是如何处理这些特殊情况的,而且根据需要很容易添加或删除这些情况。它可以正确解析你提供的示例段落。它也能正确解析下面这个测试段落(里面包含了更多特殊情况):
这是第一句。第二句!第三句?句子“第四”。句子“第五”!句子“第六”?句子“第七。”句子‘第八!’琼斯博士说:“史密斯夫人,你有一个可爱的女儿!”
不过要注意,还有其他一些特殊情况可能会导致错误,Riccardo Murri已经正确指出了这一点。
你发的例子中的第一句话是用双引号 "
括起来的,而结束的引号紧接着句号出现:infections."
你的正则表达式 [.!?]\s{1,2}
是在寻找一个句号后面跟着一个或两个空格作为句子的结束符,所以它无法捕捉到这个情况。
可以调整这个表达式来处理这种情况,允许可选的结束引号:
sentenceEnders = re.compile(r'''[.!?]['"]?\s{1,2}(?=[A-Z])''')
不过,使用上面的正则表达式会把句子的结束引号去掉。保留它稍微复杂一点,可以通过使用回顾断言来实现:
sentenceEnders = re.compile(r'''(?<=[.!?]['"\s])\s*(?=[A-Z])''')
不过要注意,基于正则表达式的分割器在很多情况下会失败,例如:
缩写:"在 Dr. A. B. Givental 的作品中 ..." -- 根据你的正则表达式,这会错误地在 "Dr."、"A." 和 "B." 后面进行分割(你可以调整单字母的情况,但除非你硬编码缩写,否则无法检测到缩写)。
句子中间使用感叹号:"... 当时,瞧!M. Deshayes 自己出现了..."
使用多个引号和嵌套引号等等。