从零开始学正则(五)

 壹 ❀ 引

我在  从零开始学正则(四) 一文中讲述了正则匹配的回溯法,以正则匹配过程引出了正则书写也会存在性能问题,并阐述了贪婪匹配,惰性匹配以及分支匹配时与回溯的中中关系。当然,对于初学者而言除了能写出正则以外,能读懂任意一段正则也是非常重要的。那么本篇文章主要针对正则表达式拆分展开分析,相信大家在阅读之后再面对各种变态长度的正则时,都能有理可据,化繁为简的拆分理解。

说在前面,正则学习系列文章均为我阅读 老姚《JavaScript正则迷你书》的读书笔记,文中所有正则图解均使用regulex制作。那么本文开始!

 贰 ❀ 正则的解构与操作符

编程语言一般都有操作符(百科),但只要说到操作符就不得不讨论操作符的优先级,因为一堆操作符在一起,系统自己也得知道谁该先执行,谁要后执行。

那么正则中的操作符是什么呢?正则中的操作符体现在正则结构中,而结构又由特殊字符与普通字符构成。

JavaScript中的正则结构大致有这些:字符字面量、字符组、量词、锚、分组、分支、反向引用。也就是前几章节讲过的知识点,我们简单复习一遍:

字符字面量:当我们具体匹配某个字符时所写的正则字段,比如a匹配字段“a”,123匹配字段“123”,\. 匹配小数点等。

字符组:当某个位置的字符可能是多种情况之一时,比如匹配任意一个数字,可以使用字符组[0-9],可简写为\d。除此之外还有反义字符组,比如匹配除了数字之外的任意字符,可以用[^0-9],可简写为\D。

量词:当某个字符需要出现多次时可使用量词加以修饰,比如数字可能出现1次或更多次,可以写成\d{1,},简写便是\d+。

锚:如果说字符字面量以及字符组是匹配的基础,那么锚的功能也是如此,只是前者用于匹配字符,而锚用于匹配位置。比如我们要匹配数字前面的位置可以写(?=\d)。

分组:分组表示一个整体,使用圆括号()表示,比如(ab)+表示字母ab至少会出现一次。

分支:分支使用管道符 | 实现,比如分支 12|34,正则会先尝试使用12进行匹配,如果行不通就会切换成分支34进行匹配,注意分支是惰性匹配。

其中涉及到的操作符以及优先级如下:

操作符描述 操作符 优先级
转义符 \ 1
圆括号和方括号 (p)、(?:p)、(?=p)、(?!p)、[p] 2
量词 {m}、{m,n}、{m,}、?、+、* 3
位置和序列 ^、$、\元字符、一般字符 4
管道符 | 5

知道了这些,我们来从优先级的角度解析正则表达式 /ab?(c|de*)+|fg/ 

由于不存在转义符,我们通过优先级第二高的括号来拆分正则,首先分组 (c|de*) 是一个整体,而在分组中字母 e 后紧跟了一个量词 *,因此 e* 是一个整体,表示e会出现任意次。最后括号中使用了优先级最低的管道符,所以正则在运行时,一定是先看字母 c ,再看 de*,这里形成了分支。

分析完分组中的内容,再看其它部分,可以得到a、b?、(c|de*)+、f、g这些组合结构,而 ab?(c|de*)+ 和 fg 又使用管道符分割,所以这是两个比较大的分组。当然就算不这么拆分,通过前面的学习解析起来也不是什么难事。我们结合图解来确认下:

 叁 ❀ 结构与操作符可能犯错的点

1.分支不使用分组包裹

在匹配整个字符串时,我们通常会添加脱字符^与美元符$,如果我们要匹配字符 abc 或 def 时,容易写成 /^abc|def$/,而此时正则的意思是匹配分支^abc或者def$,它的图解是这样:

而正确的写法应该是利用分组将分支结构进行包裹,所以应该是这样 ,图解为:

2.量词接量词

这个是我前面常犯得错误....量词与量词不能紧接着写。比如我们希望匹配数字1,2,3其一,,且这段数字长度为3的倍数,正则很容易写成 /^[123]{3}+$/ ,然后你会收到浏览器红色问候:

var regex = /^[123]{3}+$/;
regex.test(123123);

而正确的写法是 [abc]{3} 是一个整体,所以需要使用分组的括号包裹,完整就是这样:

var regex = /^([123]{3})+$/;
regex.test(123123);// true

3.元字符转义问题

正则中的元字符包括 ^、$、.、*、+、?、|、\、/、(、)、[、]、{、}、=、!、:、- 等。在我们需要匹配这些字符时,到底需不需要转义呢?当然全转义肯定也不会有问题:

var string = "^$.*+?|\\/[]{}=!:-,";
var regex = /\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,/;
regex.test(string); //true

开发中是否转义还是根据实际情况决定,比如我们真的要匹配字符 “[123]” 时 ,可以在 [ 前添加转义符,表示这不是一个反义字符组。

var string = "[123]";
var regex = /^\[123\]$/;
regex.test(string); //true

当然上述正则其实可以简写成 /^\[123]$/ ,因为 [ 前已被转义,后面的 ] 无法成对,所以可以省略转义。

假设我们要匹配 “[^123]” 时可以写成这样,注意 ^ 的转义不可少:

var string = "[^123]";
var regex = /^\[\^123\]$/;
regex.test(string); //true

同理,假设要匹配 “{1,3}”,正则我们可以写成 /\{3,5}/

注意有个特例,当匹配圆括号时,前后转义符都得加,因为只写一个会报错。看个例子:

var string = "(123)";
var regex = /^\(123\)$/;
regex.test(string); //true

 肆 ❀ 总

这一章节内容的内容还算简单,了解正则的操作符划分对于复杂的正则拆分非常有帮助。当然在正则熟练后,我们甚至能一眼看出正则所传递的匹配含义。我们通过思维导图简单做个整理:

最后留两个思考题,请写出匹配身份证的正则,以及尝试分析正则 /^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/

那么本文就写到这里,我要学习第六章节了。

猜你喜欢

转载自www.cnblogs.com/echolun/p/12084046.html