前言
最近在重新学习正则表达式,把在学习过程中所遇到的案例,还有比较难理解概念用自己的理解分析并整理总结。
如有哪些地方不对,欢迎指正,谢谢!(๑•ᴗ•๑)
本系列使用的JavaScript
所支持的正则表达式语法,并推荐你使用 regexr.com/ 去做相应的练习。
假定你已经熟悉元字符
,方括号
,修饰符
,量词
及RegExp对象
。
如果没有,请先看一遍文档
www.w3school.com.cn/jsref/jsref…
本系列同步GitHub github.com/Janking/Blo…
系列目录
案例分析
(1) 重复分组
在上一篇介绍千分位的时候,就有用到重复分组,这是一个很容易理解错误的点,至少我开始的时候是理解错误的。
假定用这个正则去匹配 1234567890
,开始我以为分组小括号(\d\d\d)
的最终结果是 123
,456
,789
都能拿到,但结果却只有789
重复分组的匹配在每次引擎退出该分组的时候被捕获,并会覆盖该分组在之前匹配的任何文本
模拟一下引擎工作的步骤:
1. 第一次匹配,捕获到 `123`,退出分组
2. 第二次匹配,捕获到 `456`, 覆盖上一次捕获的`123`,退出分组
3. 第三次匹配,捕获到 `789`,覆盖上一次捕获的`456`,退出分组
4. 退出重复分组,结束复制代码
因为重复分组最后一次循环存储的是789,另外两次分组匹配,也就是123,456是无法被获取的。
如果想要获得所有结果,就要把重复匹配放进分组中 /((\d\d\d){3})/
(2) 获取<p>...</p>
中的内容
/<p>(.*?)<\/p>/g.exec('<p>Hello,<em>world</em></p><p>Hello,Janking</p>')
运行结果
[
"<p>Hello,<em>world</em></p>",
"Hello,<em>world</em>",
index: 0,
input: "<p>Hello,<em>world</em></p><p>Hello,Janking</p>"
]复制代码
假如没有?
,而匹配结果就会差很远,不信你看!
/<p>(.*)<\/p>/g.exec('<p>Hello,<em>world</em></p><p>Hello,Janking</p>')
运行结果
[
"<p>Hello,<em>world</em></p><p>Hello,Janking</p>",
"Hello,<em>world</em></p><p>Hello,Janking",
index: 0,
input: "<p>Hello,<em>world</em></p><p>Hello,Janking</p>"
]复制代码
缺少了问号?
,结果把中间的</p><p>
也匹配进来了,为什么一个字符不同,匹配结果就差异那么大?这里涉及到正则表达式中比较重要的概念:贪婪匹配,懒惰匹配,回溯
贪婪匹配
属于贪婪模式的量词,也叫做匹配优先量词,包括:{m,n}
,{m,}
,?
,*
和 +
。
惰性匹配
在匹配优先量词后加上?
,即变成属于惰性匹配的量词,也叫做忽略优先量词,包括:{m,n}?
,{m,}?
,??
,*?
和 +?
。
回溯
当前前面分支/重复匹配成功后,没有多余的文本可被正则后半部分匹配时,会产生回溯
用一个简单的例子来解释一下贪婪匹配和惰性匹配!
贪婪 : /\d+\b/
惰性 : /d+?\b/
文本 : 1234a
贪婪正则匹配 1234a
时的过程是这样的:
1. \d+ 匹配得到 1234
2. \b 却匹配失败,因为它的左边必须是数字,而不是字母 (\b 是分词边界匹配,用来获取位置,而不是文本,上一节有讲到)
4. 这个时候,\d+会尝试回吐一个字符,即匹配结果为 123 ,可\b还是匹配失败!
5. 那就继续回吐,一直到 1,还是匹配失败,那么这个正则就整体匹配失败了
6. 这个回吐匹配结果的过程就是回溯复制代码
惰性正则匹配 1234a
时的过程是这样的:
1. \d+? 首先匹配,结果是 1 ,紧接着 \b 匹配失败
2. 那就 \d+? 继续匹配,结果是 12 ,紧接着 \b 还是匹配失败
3. \d+? 一直匹配到1234,紧接着的 \b 依然匹配失败
4. 结果整个正则匹配不成功复制代码
通过这两个例子的比较,相信你会猜到回溯会影响匹配速度,回溯的过程慢那是相对那些DFA引擎。
而JS的正则引擎是NFA(非确定型有限自动机),匹配慢,编译快。
结束语
本章节将的案例只有两个,主要是涉及到几个重要概念,占了一定的篇幅,由于本人不喜欢把文章写的太长,影响阅读体验,所以就此打住,从下一篇开始会重点的去分析常用正则表达式的匹配过程,敬请期待。
参考文献:
- 《精通正则表达式》
- 《正则表达式经典实例》