正则表达式OR分支顺序敏感原因探究——以nltk.regexp_tokenize为例
为什么正则表达式的OR分支会表现出顺序敏感性?
这事儿的核心原因是:正则里的|(OR)分支是按书写顺序逐个尝试匹配的——只要某个分支在当前文本位置匹配成功,就直接敲定这个结果,不会再继续检查后面的分支。咱们结合你给出的nltk.regexp_tokenize例子,拆解具体逻辑:
先看第一个正则的匹配过程
第一个正则的分支顺序是:
- 匹配大写字母加句号的缩写(
(?:[A-Z]\.)+,比如U.S.A.) - 匹配单词(包括带连字符的,
\w+(?:-\w+)*,比如poster-print) - 匹配整数或小数(
\d+(?:\.\d+)?)
这里藏了个容易忽略的点:正则里的\w默认是[A-Za-z0-9_],也就是说数字也属于\w的匹配范围。当处理文本里的12.40时:
- 第一个分支显然不匹配数字,直接跳过
- 轮到第二个分支时,
\w+会先把12匹配上(毕竟数字是\w的一部分),匹配成功后,正则就把12当成一个token输出,完全不会再去检查第三个分支的小数模式 - 剩下的
.40里,.不属于任何分支的匹配内容被跳过,40又会被第二个分支的\w+匹配,所以最终输出里12.40被拆成了12和40
再看调整顺序后的匹配逻辑
调整后的分支顺序把小数模式放到了单词模式前面:
- 匹配大写缩写
- 匹配整数或小数
- 匹配单词
这时候处理12.40就不一样了:
- 第一个分支不匹配,跳过
- 第二个分支
\d+(?:\.\d+)?正好能完整匹配12.40(整数部分12加上小数部分.40),匹配成功后直接输出这个完整的小数token,不会再去碰后面的单词模式 - 同理
13.10也会被完整匹配,所以输出里是12.40和13.10
关键总结
正则OR分支的顺序直接决定了匹配优先级:更具体、更特殊的模式一定要放在更宽泛的模式前面,不然就会被宽泛的模式"截胡"。在这个例子里,小数模式比\w+(能匹配数字、字母、下划线)更特殊,所以把它提前,才能保证完整的小数被正确识别,而不是被拆成零散的数字片段。
内容的提问来源于stack exchange,提问作者Maciej Wasilewski
相关产品推荐
相关产品推荐

