使用Python正则表达式进行词元化

15 投票
2 回答
21701 浏览
提问于 2025-04-16 18:46

我正在尝试在Python中将字符串拆分成“标签”的列表。拆分时需要处理像“HappyBirthday”这样的字符串,并去掉大部分标点符号,但要保留连字符和撇号。我的起点是:

tags = re.findall("([A-Z]{2,}(?=[A-Z]|$)|[A-Z][a-z]*)|\w+-\w+|[\w']+"

我想把这个示例数据:

Jeff's dog is un-American SomeTimes! BUT NOTAlways

变成:

['Jeff's', 'dog', 'is', 'un-American', 'Some', 'Times', 'BUT', 'NOT', 'Always']

附言:抱歉我的描述不太好。我不太确定怎么解释,而且在谷歌上也没找到太多帮助。希望这个例子能清楚地说明我的意思。

编辑:我觉得我需要更准确一些,所以还有:

  1. 如果一个词是用连字符连接并且是大写的,比如'UN-American',那么它应该保持为一个词,输出就是'UN-American'
  2. 如果连字符两边有空格,比如'THIS- is'或'This - is',那么应该忽略连字符,分别输出["THIS", "is"]和["This", "is"]。
  3. 同样地,如果撇号在一个词的中间,比如"What'sItCalled",那么应该输出["What's", "It", "Called"]。

2 个回答

3

为了处理你编辑过的案例,我会对phynfo的精彩回答做一些修改。

>>> s = """Jeff's UN-American Un-American un-American 
           SomeTimes! BUT NOTAlways This- THIS- 
           What'sItCalled someTimes"""
>>> re.findall("[A-Z\-\']{2,}(?![a-z])|[A-Z\-\'][a-z\-\']+(?=[A-Z])|[\'\w\-]+",s)
["Jeff's", 'UN-', 'American', 'Un-', 'American', 'un-American', 
 'Some', 'Times', 'BUT', 'NOT', 'Always', 'This-', 'THIS-', 
 "What's", 'It', 'Called' 'someTimes']

你需要清楚地定义你想要的行为规则。单纯的分词并不能算是定义,你需要有类似于phynfo所提到的规则。比如,你可以设定一个规则,让'NOTAlways'变成'NOT''Always',同时要保留连字符。因此,'UN-American'会被拆分,就像'UNAmerican'也会被拆分一样。你可以尝试定义更多的规则,但当规则重叠时,你必须明确哪个规则是适用的。

24

我建议你可以这样做:

re.findall("[A-Z]{2,}(?![a-z])|[A-Z][a-z]+(?=[A-Z])|[\'\w\-]+",s)

这样处理后,你的例子会得到:

["Jeff's", 'dog', 'is', 'un-American', 'Some', 'Times', 'BUT', 'NOT', 'Always']

解释一下:这个正则表达式由三种选择组成:

  1. [A-Z]{2,}(?![a-z]) 匹配所有字母都是大写的单词。
  2. [A-Z][a-z]+(?=[A-Z]) 匹配首字母大写的单词。这里的前瞻 (?=[A-Z]) 会在下一个大写字母之前停止匹配。
  3. [\'\w\-]+ 匹配其他所有情况,也就是可能包含 '- 的单词。

撰写回答