我需要从一大块文本中提取可能的标题。因此,例如,我希望匹配“Joe Smith”,“The Firm”或“United States of America”等词。我现在需要修改它以匹配以某种标题开头的名称(例如“Dr. Joe Smith”)。这是我的正则表达式:
NON_CAPPED_WORDS = (
# Articles
'the',
'a',
'an',
# Prepositions
'about',
'after',
'as',
'at',
'before',
'by',
'for',
'from',
'in',
'into',
'like',
'of',
'on',
'to',
'upon',
'with',
'without',
)
TITLES = (
'Dr\.',
'Mr\.',
'Mrs\.',
'Ms\.',
'Gov\.',
'Sen\.',
'Rep\.',
)
# These are words that don't match the normal title case regex, but are still allowed
# in matches
IRREGULAR_WORDS = NON_CAPPED_WORDS + TITLES
non_capped_words_re = r'[\s:,]+|'.join(IRREGULAR_WORDS)
TITLE_RE = re.compile(r"""(?P<title>([A-Z0-9&][a-zA-Z0-9]*[\s,:-]*|{0})+\s*)""".format(non_capped_words_re))
构建以下正则表达式:
(?P<title>([A-Z0-9&][a-zA-Z0-9]*[\s,:-]*|the[\s:,]+|a[\s:,]+|an[\s:,]+|about[\s:,]+|after[\s:,]+|as[\s:,]+|at[\s:,]+|before[\s:,]+|by[\s:,]+|for[\s:,]+|from[\s:,]+|in[\s:,]+|into[\s:,]+|like[\s:,]+|of[\s:,]+|on[\s:,]+|to[\s:,]+|upon[\s:,]+|with[\s:,]+|without[\s:,]+|Dr\.[\s:,]+|Mr\.[\s:,]+|Mrs\.[\s:,]+|Ms\.[\s:,]+|Gov\.[\s:,]+|Sen\.[\s:,]+|Rep\.)+\s*)
这似乎不起作用:
>>> whitelisting.TITLE_RE.findall('Dr. Joe Smith')
[('Dr', 'Dr'), ('Joe Smith', 'Smith')]
有更好的正则表达式的人可以帮我解决这个正则表达式的混乱吗?
答案 0 :(得分:2)
问题似乎是表达式的第一部分[A-Z0-9&][a-zA-Z0-9]*[\s,:-]*
正在吞噬你的“前缀标题”中的初始字符,因为它们是标题的,直到你进入句号。因此,当+
重复子表达式并遇到'Dr.'
时,表达式的初始部分与'Dr'
匹配,并且只留下不匹配的句点。
一个简单的解决方法是简单地将“特殊情况”移动到表达式的前面,因此它们被匹配为第一个手段,而不是最后的手段(这基本上只是从结尾移动{0}
表达到前面):
TITLE_RE = re.compile(r"""(?P<title>({0}|[A-Z0-9&][a-zA-Z0-9]*[\s,:-]*)+\s*)""".format(non_capped_words_re))
结果:
>>> TITLE_RE.findall('Dr. Joe Smith');
[('Dr. Joe Smith', 'Smith')]
我可能会更进一步修改表达式以避免[\s:,]+
的所有重复,但我不确定除了使格式化表达看起来更好一点之外还有任何实际好处:
'|'.join(IRREGULAR_WORDS)
TITLE_RE = re.compile(r"""(?P<title>((?:{0})[\s:,]+|[A-Z0-9&][a-zA-Z0-9]*[\s,:-]*)+\s*)""".format(non_capped_words_re))