Centos7学习笔记(十六)- 正则表达式高级部分
1、正则表达式工作流程
取得正则字符串并检查正则语法。
编译:将正则字符串编译成真正的正则表达式。
启动正则引擎开始匹配
正则引擎:逐个字符扫描字符串并与编译后的正则表达式的各个元素进行匹配。
何为正则元素呢?
纯字符组合,比如“abc123”,那么每一个字符都是一个正则元素
[a-z]这样的样式,那么视中括号[]为一个整体作为正则元素,中括号内的内容是限定的匹配范围
字符匹配的反斜线序列也是一种正则元素
字符.是一个独立的正则元素,它除了不能匹配\n,其他任意字符都能匹配
所有的*、?、{m}、{m,}、{m,n}、{,n}这几个正则,都是作为量词存在的,即计量数量,这些量词,不能作为独立的正则元素,但是是作为修饰符存在,修饰定义它前面的字符或子表达式内容。
|分隔符两端的,比如a|b这样的表达,那么竖线两端的字符或子表达式都是正则元素
()圆括号将视为一个整体正则元素,比如sed中常用的(.*)。
2、正则表达式匹配流程
正则是按每一轮的方式匹配,每轮,都要从正则的第一个元素从头开始匹配。
即,被匹配的字符串或内容被逐个字符扫描去匹配正则元素,若成功,下一字符匹配下一个正则元素,若不成功,则消耗掉已经被扫描匹配的字符,剩余的字符,进入下一轮完整匹配。
3、正则引擎
DFA类——确定有穷状态机,
将扫描到的字符与所有可能的分支一次性进行匹配
不断淘汰匹配失败的分支,最终确定一个成功的分支
DFA对字符串中的每一个字符,只扫描匹配一次,不会回溯,也没有字符交还的过程
NFA类——非确定有穷状态机
它对正则表达式中的所有分支做标记,以便匹配失败时,可以根据标记回头匹配其他分支,然后从中选择一个分支优先匹配,剩余分支先放着,在所选分支匹配失败时,再回头选择其他分支
如果某个分支匹配成功,则剩下的分支就直接丢弃
它会记录所有可能的分支,每个分支的匹配都是不确定的,失败的时候回头重新匹配其他分支
NFA有可能在匹配失败后,交还字符,然后回溯,并对某个字符进行多次匹配
DFA、NFA混合类
[root@c7study /home/study/.ssh]# echo '404 not found'|perl -nE '/not|not found/;say $&' #《========$&是表示匹配的内容 not [root@c7study /home/study/.ssh]# echo '404 not found'|perl -nE '/not found|not/;say $&' not found [root@c7study /home/study/.ssh]# echo '404 not found'|egrep 'not|not found' 404 not found
4、什么是回溯?及影响?
回溯就是在有分支或者贪婪匹配时,为了正确获取匹配结果,必须在分支匹配失败或贪婪匹配内容过多后,交还字符,然后回头重新进行字符扫描匹配。
它的显著影响是很有可能大大降低正则匹配的效率,显著的提高了匹配花费时间。

5、反斜线序列正则
①字符匹配类
\n——匹配换行符
\N——匹配非换行的字符
\w——匹配单词构成部分,等价于[_[:alnum:]]
\W——匹配非单词构成部分,等价于[^_[:alnum:]]
\s——匹配空白字符,等价于[[:space:]]
\S——匹配非空白字符,等价于[^[:space:]]
\d——匹配数字,等价于[0-9]
\D——匹配数字,等价于[^0-9]
\\——匹配\字符
②位置锚定类
\b——匹配单词边界处(开头和结尾),如\bword\b
\<——匹配单词开头位置
\>——匹配单词结尾位置,所以,\<和\>是配对使用的
\B——匹配非单词边界处的位置
\A——匹配字符串的绝对行首
\z——匹配字符串的绝对行尾
\Z——匹配字符串的行尾,如果行尾有换行符,那么匹配换行符前的位置
\G——第一次匹配的位置,全局匹配有效,也称为位置粘滞
^——匹配每一行行首
$——匹配每一行行尾,换行符前的位置
理解这些位置
那么,^$为啥能匹配空行?看下图就能理解:
图中以字符串abc\n\n123\ndef为例,既符合^又符合$位置的,只有空行那一行。
6、贪婪匹配、非贪婪匹配、占有优先匹配
上述正则元素中解释的作为修饰符存在的量词,是引起贪婪匹配的原因。那么什么是非贪婪匹配和占有优先匹配呢?先看其具体表达
| 贪婪匹配 | 非贪婪匹配 | 占有优先匹配 |
|---|---|---|
| * | *? | *+ |
| ? | ?? | ?+ |
| + | +? | ++ |
| {m} | ||
| {m,} | {m,}? | {m,}+ |
| {m,n} | {m,n}? | {m,n}+ |
| {,n} | {,n}? | {,n}+ |
非贪婪匹配时,{m}?和{m,n}?是等价的,因为最多都匹配m次。
??在非贪婪匹配时,它匹配任意一个字符、位置、空字符,但这种使用方式基本不会用
在perl中,因为不支持{,n}的方式,所以也没有对应的非贪婪匹配{,n}?和占有优先匹配{,n)+
关于{m},因为它本身是精确匹配,所以{m}?和{m}+也并无意义。
非贪婪匹配,是跟贪婪匹配完全相反的匹配。贪婪匹配是尽可能多的匹配,然后慢慢交还字符,而非贪婪匹配,则是尽可能少的匹配,然后若是不能完全匹配,则逐个消耗匹配成功的字符。


占有优先匹配,它是在贪婪匹配的基础上,所有匹配成功的字符都被消耗掉,不会再交还进行回溯。所以,等同于零宽断言。一般而言,占有优先匹配的+后不应有字符,特别是*+这个,它会消耗掉其后的所有字符元素。
7、环视锚定
环视锚定也用于匹配位置,也是一种位置锚定。位置锚定,就不会消耗字符元素。
几种写法:
(?=...)——从左向右顺序环视,这里的...表示要匹配的内容,比如a(?=\d)表示锚定a字符右边开始是数字的情况。
(?!...)——从左向右顺序环视的取反,...仍然表示要匹配的内容,但是要取反内容,比如a(?!\d)表示a字符右边不是数字的情况
(?<=...)——从右向左逆序环视,...表示要匹配的内容,比如(?<=\d)a表示字符a左边是数字的情况匹配成功
(?<!...)——从右向左逆序环视,但是匹配内容取反。比如(?<!\d)表示字符a左边是非数字情况匹配成功




8、分组捕获
分组捕获这个概念,其实就是sed命令中常用的(.*),然后反向引用\1这种形式。用()来把内容视为一个整体,然后“捕获”,并可以引用。
分组捕获后匹配的结果,会被保存在变量中,可以使用\n或$N(perl支持)来取得各分组捕获所匹配的结果。
还有一些分组捕获的方式,主要是在perl中支持。下面来分类解释:
①命名捕获
命名捕获的意思是将捕获匹配的内容,独立命名,使捕获内容既可以传统引用,又可以用命名引用。在引用的时候,可以用传统的\n或$n方式引用,也可以用\k<名字>的方式引用。在正则外部,比如perl中,可以用$+{名字}
使用形式是(?<NAME>pattern)。其中,NAME是要定义的名字,pattern是需捕获内容。
在引用的时候,可以用传统的\n或$n方式引用,也可以用\k<NAME>的方式引用。在正则外部,比如perl中,可以用$+{NAME}来引用。

②匿名捕获(只分组不捕获)
匿名捕获,即是将匹配内容只是做分组匹配,并不讲匹配内容赋予变量以引用。
使用形式:(?:pattern)
③固化分组
固化分组其功能上跟占有优先匹配(*+、?+、++、{m}+、{m,n}+、{m,}+)是等价的。匹配上了,即占有(占有字符了)了,不交还字符,不回溯了。
固化分组,也是没有捕获功能的。
使用形式:(?>pattern )

9、匹配模式的修饰符
这里的“修饰符”指的都是perl支持的方式,功能上,更像是一个开关,使原本不能匹配的方式,通过加修饰符,变的能匹配。主要用在编程语言环境,比如perl。因为编程语言环境,允许一个字符串,包含各种特殊的字符,比如\n\t这种。
有如下若干修饰符:
i:作用,忽略大小写

m:作用,让^和$能匹配含有像\n这样特殊字符的字符串的每一行行首和行尾。(注意:这不是grep、awk、sed这3个按行处理文字内容的常规处理方式)

s:作用,让.能匹配\n这样的换行符。看下图示例:

x:作用,允许正则表达式使用空白符号(甚至加#注释),免得使正则表达式晦涩难懂,这称之为free-spacing。在使用x修饰时,会使要匹配处理的内容中原本的空白符号失去意义,这时候,就必须要用\s或者[:space:]去匹配空白符号。
ans="cat sheep tiger";
ans =~ /(\w) *(\w) *(\w)/; # 正常情况下的匹配表达式
ans =~ /(\w)\s* (\w)\s* (\w)/x;
ans = ~ / (\w)\s* # 可以加上本行注释:匹配第一个单词 (\w)\s* # 可以加上本行注释:匹配第二个单词 (\w) # 可以加上本行注释:匹配第三个单词 /x;
ans =~ / (\w)\Q \E # \Q \E强制将中间的空格当作字面符号被匹配 (\w)\Q \E (\w) /x;
o:作用,使正则表达式只被编译一次,编译后的结果被缓存下来,以便循环调用。o修饰符在有循环处理正则的情景中,非常有用,能大大缩减处理时间。如下图中的示例:

g:作用,全局匹配。
1、默认情况下,正则匹配会在匹配成功一次后,就退出匹配。(grep是全局匹配的,不算在内)
2、加入g修饰符后,正则匹配会在成功匹配一次后,设置一个指针,该指针位于第一次匹配成功的结果之后。其作用是标记位置,以便下一轮全局匹配从当前标记位开始。如果第2轮或者某一轮全局匹配失败,则默认情况下,该指针会被重置到需匹配的内容(字符串或者文本内容等)的开头。

3、为了解决上述第二条指针重置的问题,引入c修饰符,使指针在匹配失败后,保持不动,后续全局匹配,能继续处理后续内容。

4、位置粘滞\G的作用。\G针对全局匹配时使用,要求指针后的内容在下一轮匹配时必须成功,否则指针重置。


5、g修饰符,基本都是用在循环结构中,以便重复匹配能够成功。


