惯例,先上原文。
之前我们学习了正则的字符匹配、位置匹配和括号的使用。接下来我们学习正则表达式中常见的一个问题:回溯问题。如果说前面是学习正则的使用,那么本节感觉是学习正则的优化。我觉得回溯其实是一种性能的浪费,所以学习写好的正则可以减少回溯的产生,提高效率。
回溯,顾名思义就是回复过去状态,可以理解为回退。老姚文章中说这是匹配原理中的一个名词,但其实实际使用很少有人会关心这个问题,甚至都没有听过这种说法。所以接下来我们学习高大上的回溯问题。
1. 回溯的理解
正则表达式匹配字符串的这种方式,有个学名,叫回溯法。
回溯法也称试探法,它的基本思想是:从问题的某一种状态(初始状态)出发,搜索从这种状态出发所能达到的所有“状态”,当一条路走到“尽头”的时候(不能再前进),再后退一步或若干步,从另一种可能“状态”出发,继续搜索,直到所有的“路径”(状态)都试探过。这种不断“前进”、不断“回溯”寻找解的方法,就称作“回溯法”。(copy于百度百科)。
本质上就是深度优先搜索算法。其中退到之前的某一步这一过程,我们称为“回溯”。从上面的描述过程中,可以看出,路走不通时,就会发生“回溯”。即,尝试匹配失败时,接下来的一步通常就是回溯。
以上是原文中的解释,组织不了更好的语言索性直接贴过来了。
2. 回溯的例子
为了精简的总结,直接再贴个原文中的图过来。图表示的是用正则 /ab{1,3}bbc/
匹配字符串 abbbc
的过程:
从图上可以很清晰的看出来是因为贪婪量词 b{1,3}
的原因导致的回溯。但其实不只是贪婪量词可以导致回溯,不贪婪的惰性量词也会导致回溯,如下是使用 /^\d{1,3}?\d{1,3}$/
匹配 12345
的过程:
可以看到因为惰性量词一开始就很“满足”了,所以导致整体的匹配必须重来,所以哪怕是满足了也必须多拿点,这样为了整体才能成功!
除了这两种情况,分支结构也可以导致回溯的发生,以下是使用 /can|candy/
匹配字符串 candy
的过程:
可以看到由于先满足了 can
所以就尝试着匹配之后的内容,但整体进行不下去了,所以必须回溯去匹配 candy
才能满足条件。
JS的正则引擎是NFA,NFA是“非确定型有限自动机”的简写。大部分语言中的正则都是NFA,除此之外还有DFA引擎。(知识点,哈哈,单列)。
至此,学习正则表达式的过程就告一段落了,其实老姚的文章后面还有内容,但是作为学习笔记总结,我觉得到这里就ok了,后面的内容有兴趣的童鞋可以去老姚的原文看哦 https://juejin.im/post/5965943ff265da6c30653879