正则表达式匹配原理:
本章节将会简单的介绍一下正则表达式匹配原理,这将有助于书写更为有效率的正则表达式,而不仅仅能够完成匹配任务。
一.关于正则表达式引擎:
正则引擎大体上可分为不同的两类:DFA和NFA。
1.DFA是Deterministic finite automaton的缩写,确定型有穷自动机。
2.NFA是Non-deterministic finite automaton的缩写,非确定型有穷自动机。NFA又可以分为两类:
1): Traditional NFA,传统型非确定有穷自动。
2): POSIX NFA,符合标准的非确定型有穷自动机。
DFA引擎不需要进行回溯,所以匹配效率一般情况下要高,但是它并不支持捕获组,于是也就不支持反向引用和$这种形式的引用。
POSIX NFA是指符合POSIX 标准的NFA引擎,它的主要特点是提供longest-leftmost匹配,也就是说在找到最左侧最长匹配之前,它将继续回溯,此种引擎对非贪婪模式不支持。
当前大多数语言都是使用传统型NFA正则引擎,因为它更为强大灵活。
二.字符串的组成:
想要真正理解正则表达式的匹配原理,那么首先就要知道在正则表达式眼中,字符串是由哪些成分构成的,图示如下:
上面的图片得出,字符串不但由字符组成,还包括字符之间的位置。对于字符串"antzone"而言,它是由7个字符和8个位置构成。
三.零宽和占有字符表达式:
在正则表达式中,如果子表达式匹配到的是字符内容,而非位置,并且会被保存到匹配结果中,那么这个子表达式是占有字符的。如果子表达式匹配的是位置,或者匹配的内容不被保存在最终结果中,那么就认为这个子表达式是零宽的。
占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。
注意:这里所说的子表达式并非只有用小括号括起来的表达式,而是正则表达式中的任意匹配单元。
四.控制权的传动:
正则表达式当开始匹配的时候,一般是由一个子表达式获取控制权,从字符串中的某一个位置开始尝试匹配,一个子表达式开始尝试匹配的位置,是从前一子表达匹配成功的结束位置开始的,看下面的例子:
(子表达式一)(子表达式二)
如果子表达式一是零宽度的,那么它的匹配起始位置和结束位置是同一个位置,如果位置是0,那么子表达式二将从位置0进行匹配。
如果子表达式一是占有字符的,开始匹配位置0,结束匹配位置是4,那么子表达式二的开始匹配位置就是4。
五.正则表达式匹配分解:
分解演示一:
1.将要进行匹配的字符串:"antzone"。
2.正则表达式:/zone/。
匹配过程如下:
首先由正则表达式中字符"z"取得控制权,然后去尝试匹配字符串中的第一个字符"a",匹配失败,则继续尝试第二个字符"n"依然会失败,直到字符"z",匹配成功,这样控制权由正则表达式中的字符"z"传递给字符"o",由于字符"z"已经被匹配,所以字符"o"将会从位置4开始匹配,由"o"来匹配"o",匹配成功,这样依次传递下去,最终正则表达式匹配成功,匹配结果为"zone"。
实例如下:
var str="antzone"; var reg=/zone/; console.log(str.match(reg));
分解演示二:
1.将要进行匹配的字符串:"antzone"。
2.正则表达式:/\ban?t/。
匹配过程如下:
首先由正则表达式中的元字符\b取得控制权,开始去尝试匹配字符串"antzone",它匹配的是单词边界位置,匹配成功后,控制权交给正则表达式字符"a",由于"\b"是零宽的,所以"a"从位置0开始匹配,匹配字符"a"成功,然后将控制权交给"n?",由于“?”是匹配优先量词(同时记录一个备选状态,如果匹配不成功则进行回溯),所以会先尝试进行匹配,能够成功"n",控制权然后交给字符"t",也能够匹配成功,匹配结果为:"ant"。
实例如下:
var str="antzone"; var reg=/\ban?t/; console.log(str.match(reg));