正则表达式学习笔记

2018-03-16  本文已影响23人  Rui哥

原文参考自: https://www.jianshu.com/p/681d3e07fb0f

一、原理概论

1、正则引擎 大致可以分为两类:DFA \ NFA

2.1、知识储备

这一小节对于你理解正则表达式很有用,尤其是明白什么是字符,什么是位置。

正则眼中的字符串.png

在上面的字符串中,一共有8个字符,这是你能看到的,还有9个位置 。为什么要有字符还要有位置呢?因为位置是可以被匹配的。

那么进一步我们再来理解“占有字符”“零宽度”:

占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。
举个栗子,比如/aa/是匹配不了a的,这个字符串中的a只能由正则的第一个a字符匹配,而不能同时由第二个a匹配(废话)。但是位置是可以多个匹配的,比如/\b\ba/是可以匹配a的,虽然正则表达式里有2个表示单词开头位置的\b元字符,这两个\b是可以同时匹配位置0(在这个例子中)的。

注意:我们说字符和位置是面向字符串说的,而说占有字符和零宽度是面向正则说的。

2.2 控制权和传动

举一个栗子,read(?=ing)ing\sbook匹配reading book,我们把这个正则看成5个子表达式read(?=ing)ing\sbook,当然你也可以吧read看做4个单独字符的子表达式,只是我们这里为了方便这么看待。read从位置0开始匹配到位置4,后面的(?=ing)继续从位置4开始匹配,发现位置4后面确实是ing,于是断言匹配成功,也就是整一个(?=ing)就是匹配了位置4这一个位置而已(这里更能理解什么是零宽了吧),然后后面的ing再从位置4开始匹配到位置7,然后\s再从位置7匹配到位置8,最后的book从位置8匹配到位置12,整一个匹配完成。

3. 匹配之旅“浅”度游(可跳过)

说了那么多,我们把自己当做一个正则引擎,一步一步以最小的单位——“字符”“位置”——去看一下正则匹配的过程,举几个栗子。

3.1 基本匹配

正则表达式:easy
源字符串:So easy

匹配过程:首先由正则表达式字符e取得控制权,从字符串的位置0开始匹配,遇到字符串字符‘S’,匹配失败,然后正则引擎向前传动,从位置1开始尝试,遇到字符串字符‘o’,匹配失败,继续传动,后面的空格自然也失败,于是从位置3开始尝试匹配,成功匹配字符串字符‘e’,控制权交给正则表达式子表达式(这里也是一个字符)‘a’,尝试从上次匹配成功的结束位置4开始匹配,成功匹配字符串字符‘a’,后面一直如此匹配到‘y’,然后匹配完成,匹配结果为easy

3.2 零宽匹配

正则:^(?=[aeiou])[a-z]+$
源字符串:apple

首先这个正则表示:匹配这样一个从头到尾完整的字符串,这整一个字符串仅由小写字母组成,并且以a、e、i、o、u这5个字母任一字母开头。

匹配过程:首先正则的^(表示字符串开始的位置)获取控制权,从位置0开始匹配,匹配成功,控制权交给(?=[aeiou]),这个子表达式要求该位置右边必须是元音小写字母中的一个,零宽子表达式相互间不互斥,所以从位置0开始尝试匹配,右侧是字符串的‘a’,符合因此匹配成功,所以(?=[aeiou])匹配此处的位置0匹配成功,控制权交给[a-z]+,从位置0开始匹配,字符串‘apple’中的每个字符都匹配成功,匹配到字符串末尾,控制权交回正则的$,尝试匹配字符串结束位置,成功,至此,整个匹配完成。

3.3 贪婪匹配和非贪婪匹配

正则1:{,*}
正则2:{,*?}
源字符串:{233}

这里有两个正则,在限定符(语法会讲什么是限定符)后面加?符号表示忽略优先量词,也就是非贪婪匹配,这个栗子我剥得快一点。

首先开头的{匹配两个正则都是一样的表现。

正则1的.*为贪婪匹配,所以一直匹配余下字符串'233}',匹配到字符串结束位置,只是每次匹配,都记录一个备选状态,为了以后回溯,每次匹配有两条路,选择了匹配这条路,但记一下这里还可以有不匹配这条路,如果前面死胡同了,可以退回来,此时控制权交还给正则的},去匹配字符串结束位置,失败,于是回溯,意思就是说前面的.*你吃的太多了,吐一个出来,于是控制权回给.*,吐出一个},(其实是用了前面记录的备选状态,尝试不用.*去匹配'}'),控制权再给正则的},这次匹配就成功了。

正则2的.*?为非贪婪匹配,尽可能少地匹配,所以匹配'233}'的每一个字符的时候,都是尝试不匹配,但是一但控制权交还给最后的}就发现出问题了,赶紧回溯乖乖匹配,于是每一个字符都如此,最终匹配成功。

其它详细参考:http://blog.csdn.net/lxcnn

二、语法一览

其它正则入门参考:deerchao写的30分钟入门教程

1、常见字符--简单元字符

为什么这里要加简单2个字,因为在正则中,\d\w这样的叫元字符,而{n,m}(?!exp)这样的也叫元字符,所以元字符是在正则中有特定意义的标识,而这一小节讲的是简单的一些元字符。

2. 要表示出现次数(重复)——限定符

3. 匹配位置——定位符和零宽断言

匹配某个位置的表达式都是零宽的,这是主要包含两部分,一是定位符,匹配一个特定位置,二是零宽断言,匹配一个要满足某要求的位置。

定位符有以下几个常用的:

零宽断言(JS支持的)有以下两个:

4. 想表达“或”的意思——字符簇和分歧

我们经常会表达“或”的含义,比如这几个字符中的任意一个都行,再比如匹配5个数字或者5个字母都行等等需求。

字符簇可用来表达字符级别的“或”语义,表示的是方括号中的字符任选一:

分歧用来表达表达式级别的“或”语义,表示的是匹配|左右任一表达就可:

5. 想表达“非”的意思——反义

有时候我们想表达“除了某些字符之外”这样的需求,这个时候就要用到反义.

-[^aeiou]表示除了aeiou外的任一字符,在方括号中且出现在开头位置的^表示排除,如果^在方括号中不出现在开头位置,那么它仅仅代表^字符本身.

6. 整体看待和捕获——分组和后向引用

其实你在上面的一些地方已经看到了圆括号,是的,圆括号就是用来分组的,括在一对括号里的就是一个分组。

上面讲的大部分是针对字符级别的,比如重复字母 “A” 5次,可以用A{5}来表示,但是如果想要字符串“ABC”重复5次呢?这个时候就需要用到括号。

括号的第一个作用,将括起来的分组当做一个整体看待,所以你可以像对待字符重复一样在一个分组后面加限定符,比如(ABC){5}

分组匹配到的内容也就是这个分组捕获到的内容,从左往右,以左括号为标志,每个分组会自动拥有一个从1开始的编号,编号0的分组对应整个正则表达式,JS不支持捕获组显示命名。

括号的第二个作用,分组捕获到的内容,可以在之后通过\分组编号的形式进行后向引用。比如(ab|cd)123\1可以匹配“ab123ab”或者“cd123cd”,但是不能匹配“ab123cd”“cd123ab”,这里有一对括号,也是第一对括号,所以编号为捕获组1,然后在正则中通过\1去引用了捕获组1的捕获的内容,这叫后向引用。

括号的第三个作用,改变优先级,比如abc|de(abc|d)e表达的完全不是一个意思。

7. 转义

任何在正则表达式中有作用的字符都建议转义,哪怕有些情况下不转义也能正确,比如[]中的圆括号、^符号等。

8. 优先级问题

优先级从高到低是:

9. 贪婪和非贪婪

在限定符中,除了{n}确切表示重复几次,其余的都是一个有下限的范围。

10. 修饰符(匹配选项)

其实正则的匹配选项有很多可选,不同的宿主语言环境下可能各有不同,此处就JS的修饰符作一个说明:




元字符 描述
\ 将下一个字符标记符、或一个向后引用、或一个八进制转义符。例如,“\n”匹配\n。“\n”匹配换行符。序列“\”匹配“\”而“(”则匹配“(”。即相当于多种编程语言中都有的“转义字符”的概念。
^ 匹配输入字行首。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
$ 匹配输入行尾。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
* 匹配前面的子表达式任意次。例如,zo能匹配“z”,也能匹配“zo”以及“zoo”。等价于o{0,}
+ 匹配前面的子表达式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
? 匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。
{n} n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o为一组,后三个o为一组。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+”将尽可能多的匹配“o”,得到结果[“oooo”],而“o+?”将尽可能少的匹配“o”,得到结果 ['o', 'o', 'o', 'o']
.点 匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“[\s\S]”的模式。
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”。

详见百度百科URL: "https://baike.baidu.com/item/正则表达式/1700215?fr=aladdin"

上一篇 下一篇

猜你喜欢

热点阅读