分词时如何避免相似正则表达式模式的匹配重叠?
解决令牌重叠的分词方案
最长匹配原则(最常用的高效方案)
这是词法分析领域的经典解决方案,核心逻辑非常直接:当多个令牌规则能匹配当前位置的字符时,优先选择匹配长度最长的那个。
具体实现步骤:
- 把所有令牌的正则表达式按「匹配长度从长到短」排序,比如先放
===、<=、=>这类长令牌,再放==、<、=这类短令牌 - 维护一个扫描指针
pos,从字符串起始位置(0)开始 - 从
pos位置出发,依次尝试匹配排序后的正则规则:- 若匹配成功,生成对应令牌,同时把
pos跳到匹配结束的索引位置 - 若未匹配到,再尝试下一个更短的规则
- 若匹配成功,生成对应令牌,同时把
- 全程不需要修改原字符串,只靠指针跟踪扫描位置,效率远高于你之前的替换方案
举个实际例子:扫描<=时,先尝试匹配长度为2的LTEQ规则,匹配成功后直接生成LTEQ令牌,不会再去匹配长度为1的LT规则,从根源避免了部分匹配的问题。
手动字符级扫描(适合复杂场景)
如果正则表达式的方式满足不了特殊需求(比如某些重叠逻辑更复杂),可以手动逐字符扫描:
- 同样维护扫描指针
pos,从0开始 - 读取当前字符后,预判后续字符是否能组成更长的令牌:
- 比如读到
<,就检查pos+1位置的字符是不是=,若是则生成LTEQ令牌,pos +=2;若否则生成LT令牌,pos +=1 - 再比如处理
=>和>=的冲突:读到=时,检查pos+1是否为>,若是则生成ARROW令牌;读到>时,检查pos-1是否为=,若不是则生成GT令牌
- 比如读到
- 这种方式完全可控,没有正则的额外开销,能处理各种边缘重叠场景。
对你提出的两种思路的分析
- 记录已使用字符区间:这个思路可行,但实现复杂度高于最长匹配——每次匹配都要检查当前位置是否在已占用区间,不如直接维护扫描指针高效。如果用正则的话,只要每次从
pos位置开始匹配,而非全局匹配,就完全不需要记录区间。 - 先拆简单令牌再组合:这种方式本质是把词法分析的部分逻辑转移到语法分析阶段,比如先把
<和=拆成两个单独令牌,再在语法分析时识别<=为整体。但这会大幅增加语法分析的复杂度,且像===这类场景很难靠语法组合(==+=和===语义完全不同),不推荐使用。
总结
优先采用最长匹配原则,实现简单且高效,绝大多数成熟词法分析器(如Flex)都是基于这个逻辑。只要正则库支持从指定位置开始匹配,就能彻底规避你之前方案的效率问题和兼容性限制。
内容的提问来源于stack exchange,提问作者Jam
相关产品推荐
相关产品推荐

