Ruby的string.sub()为何非贪婪?相关匹配行为解析
Ruby 2.4.2中
sub搭配正则的行为拆解 咱们来逐个分析你遇到的这些案例,核心是搞懂sub的工作逻辑和正则匹配的优先级:
先明确两个关键前提
sub方法只替换第一个匹配到的结果,正则引擎会从字符串最左端开始扫描,找到第一个有效匹配就停止。- 正则里的量词(
*/+):*允许匹配0次或多次,+要求至少匹配1次;默认都是贪婪模式,但贪婪是在同一个匹配位置尽可能取最长,而不是跳过前面的位置找后面的长匹配。
案例1:"hello\r\n".sub(/e*/, "") => "hello\r\n"
/e*/的意思是「匹配0个或多个e」。引擎从字符串第一个字符h开始扫描:
- 这里没有e,所以能匹配到0个e(
*允许0次),这是第一个有效匹配。 sub替换这个0长度的匹配,相当于没改动原字符串,所以结果还是hello\r\n。- 为什么不匹配后面的
e?因为引擎已经在开头找到了第一个有效匹配(0次),不会继续往后扫描了。
案例2:"hello\r\n".sub(/h*/, "") => "ello\r\n"
/h*/匹配0个或多个h。引擎从开头扫描:
- 第一个字符就是
h,贪婪模式下会尽可能匹配最多的h(这里是1个),所以匹配结果是h。 sub把这个h替换成空,自然就得到ello\r\n。
案例3:"hello\r\n".sub(/e+/, "") => "hllo\r\n"
/e+/要求至少匹配1个e,所以0次匹配是无效的。引擎从开头扫描:
- 第一个字符
h不符合,继续往后找,直到遇到e,匹配这个单个e。 - 替换后就去掉了这个e,结果变成
hllo\r\n。 - 这和
/e*/的区别就是+不允许0次匹配,所以不会在开头浪费一次匹配机会,必须找到真实存在的e才行。
案例4:"hello\r\n".sub(/(\r|\n)*/, "") => "hello\r\n"
和案例1逻辑完全一致:/(\r|\n)*/匹配0个或多个换行/回车。
- 开头的
h不是换行符,所以匹配0次,sub替换后字符串无变化,哪怕后面有\r\n也不会被处理。
案例5:"hello\r\n".sub(/(\r|\n)+/, "") => "hello"
/(\r|\n)+要求至少匹配1个换行/回车,所以开头的字符不符合,引擎往后扫描:
- 找到最后的
\r\n,匹配这两个字符,替换成空,结果就是hello。
额外补充:为什么gsub不会有这种问题?
gsub会替换所有匹配到的结果,哪怕开头有0次匹配,它也会继续往后扫描其他匹配项。比如"hello\r\n".gsub(/e*/, "")会把开头的0次匹配(无变化)、中间的e、结尾的0次匹配都处理,最终结果是hllo\r\n(本质是替换了中间的e,因为0次匹配替换后还是原内容)。
内容的提问来源于stack exchange,提问作者nonopolarity
相关产品推荐
相关产品推荐

