如何做论文网站,标识标牌,网页设计工作内容怎么写,微信商城登录平台需要一定的正则基础#xff0c;并且是基于JS写的文章。
正则表达式是从左往右匹配的。在使用正则表达式的时候我们知道/.*/可以匹配一个字字符串中所有的字符#xff0c;/.*?/却一个字符都匹配不到。/(.*)\d/中的.\*可以匹配除了最后一位数字的所有字符#xff0c;但是之前…需要一定的正则基础并且是基于JS写的文章。
正则表达式是从左往右匹配的。在使用正则表达式的时候我们知道/.*/可以匹配一个字字符串中所有的字符/.*?/却一个字符都匹配不到。/(.*)\d/中的.\*可以匹配除了最后一位数字的所有字符但是之前说的/.*/不是匹配了所有字符吗为什么后面的\d还可以匹配到一个数字字符
首先我们要知道对于贪婪模式在进行匹配的时候会首先尝试匹配。意思就是/.*/匹配”abcd”的时候可以选择匹配a和不匹配a都是可以的但是因为是贪婪模式所以选择了匹配ab和c和d是同样的道理到最后匹配完了abcd正则表达式匹配完成并且匹配成功。
对于懒惰模式在进行匹配的时候会首先尝试跳过。就是/.*?/匹配”abcd”字符串的时候首先尝试跳过a的匹配再跳过b的匹配直到最后正则表达式匹配完成射门都没匹配到。
通过上面的描述可以看出贪婪和懒惰只是在每一个字符是否匹配上做的选择不同相同的是在匹配和跳过的选择中正则表达式都会记住我在这里做了选择这里还要其他选择。记住这些选择的作用就是当前选择如果走不通了那么还可以回退到这里选择记录下来的另一条路这就是回溯。 开始回溯的会选择离当前位置最近的一次选择就是一个后入先出的栈的模式。
例1/(.*)\d/匹配”abcd1”
第一步因为.*是贪婪模式所以会匹配字符a并记住也可以不匹配a往后匹配字符b也可不匹配一直往后匹配了字符c字符d字符1。
第二步\d匹配的时候匹配不到字符整个正则需要回溯到选择是否匹配字符1的时候之前选择了匹配现在要选择不匹配让出了字符1。
第三步\d匹配字符1完成整个正则的匹配。
var reg /(.*)\d/
var str abcd1
var res str.match(reg) // [abcd1, abcd] 可以看到分组(.*)匹配到了abcd并不包括1
例2/(.*)\d/匹配”ab1cd”
第一步因为.*是贪婪模式所以会匹配字符a并记住也可以不匹配a往后匹配字符b也可不匹配一直往后匹配了字符c字符d字符1。
第二步\d匹配的时候匹配不到字符整个正则需要回溯到选择是否匹配字符d的时候之前选择了匹配现在要选择不匹配d让出了字符d。
第三步\d匹配字符d匹配失败。继续回溯.*让出字符c。
第四步\d继续匹配字符c匹配失败。还要继续回溯.*让出字符1.
第五步\d匹配字符1完成整个正则的匹配。
var reg /(.*)\d/
var str ab1cd
var res str.match(reg) // [ab1, ab] 可以看到分组(.*)匹配到了ab并不包括1
例3/(.*?)\d/匹配”abcd1”
第一步因为.*?是懒惰模式所以不会匹配字符a并记住可以匹配a。
第二步\d匹配字符a匹配失败。回溯.*?重新选择匹配字符a并继续放弃了字符b记住可以匹配字符b。
第二步\d匹配字符b匹配失败。回溯.*?重新选择匹配字符b并继续放弃了字符c记住可以匹配字符c。
第三步\d匹配字符c匹配继续失败。继续回溯.*?重新选择匹配了字符c继续并放弃了匹配字符d记住可以匹配字符d。
第四步\d继续匹配字符d匹配失败。还要继续回溯.*?重新选择匹配字符d还是放弃了字符1记住可以匹配字符1。
第五步\d匹配字符1完成整个正则的匹配。
var reg /(.*?)\d/
var str abcd1
var res str.match(reg) // [abcd1, abcd] 可以看到分组(.*)匹配到了abcd并不包括1
小结
对比例1和例3可以发现懒惰和贪婪模式匹配的结果是相同的但是这并不意味着这两种匹配模式匹配结果是无差别的。对于两种模式匹配的字符串如果只有一个真确的匹配结果那么确是匹配得到的结果是一样的但是一步一步检查过来就会知道虽然匹配结果一样但是经过的步骤是不同的。
如果匹配的结果不止一种可能那么这两种模式匹配得到的结果就不一样了。例如将字符串“abcd1”换成”abcd11”那么这两种模式匹配到的结果就一样了。
var reg /(.*?)\d/
var reg2 /(.*)\d/
var str abcd11str.match(reg) // [abcd1, abcd]
str.match(reg2) // [abcd11, abcd1]
例4/\w?(\w?)1/匹配“a1”
第一步\w?匹配字符a记住可以不匹配字符a
第二步(\w?)匹配字符1记住可不匹配字符1
第三步1匹配不到任意字符返回之前做选择的地方(\w?)从新选择放弃了字符1什么都没有匹配
第四步1匹配了字符1完成整个正则表达式的匹配
var reg /\w?(\w?)1/
var str a1str.match(reg) // [a1, ] 数组的第二个值也就是分组1(\w?)什么都没有匹配到
通过上面的步骤可以看出当需要回溯的时候会选择当前位置最近的一次选择在重新开始而不是从整个表达式的第一次选择开始重新选择。这是一个后进先出的模式就像栈一样。
断言/环视中的回溯
首先说结论断言中的备用状态在断言匹配结束后会被丢弃整个断言只能当做一个整体存在回溯的时候不会进入断言中的备用状态。
例5/(?(.*))\11/匹配”11111”
这个正则什么都匹配不到。
第一步(?(.*))\1匹配了11111
第二步1并不能匹配到任何字符串并且准备回溯的时候发现前面并没有可回溯的状态匹配失败
第三步以为这就结束了吗还没有会从第二个1再开始匹配虽然得到的结果是一样的直到最后一个1完成匹配匹配失败什么都没有匹配到
var reg /(?(.*))\11/
var str 11111str.match(reg) // null
注这是固化分组的一种模拟固化分组指的是放弃分组中的备用状态语法是(?…)。
分支中的回溯
首先多选分支是从左到右匹配的并不会因为某个分支的或长或短就优先匹配这样就会造成分支顺序不正确就导致某些分支永远不会被匹配到。
var reg /abc|ab|abcd/
var str abcdstr.match(reg) // [abc]
匹配结果是abc并不是ab如果将分支调换位置那么得到的结果又将不一样。
var reg /abcd|abc|ab/
var str abcdstr.match(reg) // [abcd]
var reg /ab|abcd|abc/
var str abcdstr.match(reg) // [ab]
从上面的例子中可以看出多选分支的匹配是从左到右来选择分支来匹配的并且在分支之间选择的时候会记住备用的选择以备无法匹配的时候回溯到这里选择另一个分支。
例6/((aa?)|(aa))a/匹配”aa”
第一步选择分支(aa?)并且记住可以选择分支(aa)开始匹配字符aa
第二步a匹配字符a
第三步a? 匹配第二个a记住可以不匹配这个a
第四步正则中最后一个a匹配的时候发现无字符可匹配
第五步回溯到第三步选择不匹配a字符
第六步正则中最后一个a完成了匹配字符串中的a整个正则匹配完成
var reg /((aa?)|(aa))a/
var str aastr.match(reg) // [aa, a, a, undefined]
例7/((aa)|(aa?))a/匹配”aa”
第一步选择分支(aa)并且记住可以选择分支(aa?)开始匹配字符aa
第二步a匹配字符a
第三步aa 匹配第二个a
第四步正则中最后一个a匹配的时候发现无字符可匹配
第五步回溯到第一步选择分支(aa?)
第六步这时就会重复例6的步骤直到整个正则匹配成功
var reg /((aa)|(aa?))a/
var str aastr.match(reg) // [aa, a, undefined, a]
注双引号”表示的是字符串并不是字符串的一部分
参考
精通正则表达式第三版