如何让我的标题大小写正则表达式匹配前缀标题?

1 投票
1 回答
998 浏览
提问于 2025-04-16 18:26

我需要从一段文字中提取可能的标题。比如说,我想找到像“乔·史密斯”、“公司名”或者“美利坚合众国”这样的词。现在我需要修改一下,让它能够匹配以某种头衔开头的名字,比如“博士 乔·史密斯”。我现在有的正则表达式是:

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')]

有没有人能帮我修正一下这个正则表达式,让它好用一点?

1 个回答

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))

撰写回答