深入正则表达式(2):元字符/关键子/符号说明
Author:zhoulujun Date:
元字符
正则表达式使用字符串来描述字符串,也就是说跟编程语言。
正则引擎在匹配时是一个字符一个字符的匹配的,跟我们平常印象中一串一串匹配的思维是不同的,这个需要我们时刻牢记。
匹配单个字符的元字符
匹配一个字符的关键字:
元字符 | 名称 | 含义 |
---|---|---|
. | 点号 | 匹配除换行符外的任意一个字符 |
[……] | 字符组 | 匹配组内的单个字符 |
[^……] | 排除性字符组 | 匹配单个未列出的字符 |
\meta | 转义字符 | 特殊含义或转义元字符 |
英文点号可以匹配任意字符,但是不包括换行符,也就是说匹配到换行点号就匹配失败了,我们可以使用\n来匹配换行符,在java中也有MULTI_LINES来指定让点号匹配到换行符
如果我们想匹配一个文本形式的点号怎么弄呢,可以使用反斜线\来转义关键字,这样就可以匹配一个点号,而不是匹配任意字符的关键字点号了。
关于字符组需要说明一点,可以使用-来描述一个范围,比如我们想匹配数字,我们可以写一个全列[0123456789],也可以使用[0-9],字符也是一样[a-zA-z]匹配英文大小写的26个字母,如果我们想匹配横杠怎么办呢?只有一个办法,那就是把它写在紧跟左中括号后面,[-a-z]匹配横杠和a-z的所有字母,如果写在中间是会被当成关键字来解析的。
简写
正则为我们提前定义好了一些简写,使用时可以减少表达式长度。
元字符 | 名称 | 含义 |
---|---|---|
\s | [ \f\v\t\n\r] | 匹配空格,制表符,回车、换行 |
\S | 除\s之外的任何字符 | |
\w | [a-zA-Z0-9_] | 匹配一个单词 |
\W | [^a-zA-Z0-9] | 匹配除\w外的任何字符 |
\d | [0-9] | 数字 |
\D | [^0-9] | 非数字 |
注:相同字母的小写跟大写在这是互补的,也就是互为补集,不过需要注意一点,在这里面没有包含Unicode字符,不同的正则引擎对这些有不同的实现,譬如\d有的正则引擎可以匹配Unicode中的数字,也就是说中文六也是有可能匹配的,这个可以参考各自的语言工具书(不过一般这样用没啥问题)。
计数元字符
有了匹配单个字符的关键字,匹配多个就要用到计数元字符,常用的有下面这些:
元字符 | 名称 | 含义 | 区间表示 |
---|---|---|---|
? | 问号 | 匹配至多一次 | {0,1} greedy的 |
* | 星号 | 匹配任意多次,也可以不匹配 | {0,} |
+ | 加号 | 匹配至少一次 | {1,} |
{min, max} | 区间 | min <=匹配次数 <= max | grep默认不支持{元字符,可以使用-E选项 |
*?,+?,??,{min,max}? | 忽略优先量词 | 匹配尽可能少的内容 | |
*+, ++, ?+ {min,max}+ | 占有优先量词 | 匹配内容后不会交还,类似固化分组 |
?*+这三个元字符是可以使用{}来模拟的,不过在一些正则流派里不支持{}元字符,我们只能使用这三个。
在为加?的量词上,前四个都是贪婪匹配的,也就是说他们会匹配尽可能多的字符,在匹配失败的时候才会停止,这有时候很有用,但有时候会让我们没有经过深思写出来的正则匹配到错误的内容。第四列在量词后面加了?则正相反,它会匹配尽可能少的内容,是先为人后为几的,这两种方式的区别我们后面再讲,这跟表达式引擎的递归与回溯有关。
最后一列是占有优先量词,这个概念是在贪婪的基础上加了铁公鸡属性(占有),也就是说经它匹配过的字符就是它的了,不会再吐出来。后面我们在讲回溯跟固化分组时是再来回顾这个占有优先量词。
匹配位置元字符(锚点)
讲了其他一些控制字符,我们再来看下匹配位置的元字符,这里需要记住一点:
锚点不会匹配实际的文本,而是寻找特定的位置
也就是说锚点会去查看前后字符是否符合你的要求,但是并不占用字符
元字符 | 名称 | 含义 | 示例与注 |
---|---|---|---|
^ | 脱字符 | 匹配一行开头 | |
\A | 匹配文本的起始位置 | vim里匹配一个单词起始,notepad++匹配到下一个字符,暂未想到用处 | |
$ | 美元符 | 匹配一行结束位置 | |
\Z \z | 匹配文本结束位置 | ||
\< | 单词分界符 | 匹配单词开始 | java中使用\b |
\> | 单词分界符 | 匹配单词结束 | java中使用\B |
(?=……) | 顺序环视 | 匹配右侧文本 | (?=Jeffery)Jeff 位置定在J之前但后面必须是Jeffery |
(?<= ) | 逆序环视 | 匹配左侧文本 | (?=Jeff)ery 位置定在f之后,匹配ery, s/(?<=Jeff)(?=s)/'/g 将Jeffs变为Jef's |
(?!) | 否定顺序环视 | 不匹配右侧文本 | |
(?<!) | 否定逆序环视 | 不匹配左侧文本 |
^$匹配一行开头与结尾,很常见,不多说
\<与\>是单词分界符,也有使用\b与\B来分界的,在前面匹配重复单词时,我们就可以使用\<(\w+)\>\s*\1来界定单词,这样this is a test test ha ha is就不会被认为是重复单词的一个了
环视,分顺序和逆序,肯定和否定,总共四种,要求你的文本前后需要满足环视的要求
顺序环视,右侧文本必须满足给定的条件,(?=Jeffery)Jeff里要求匹配Jeff,但是右侧必须为Jeffery,也就是说只匹配Jeffery里的Jeff,其余的Jeffary等就不能匹配
逆序环视,左侧文本必须满足给定条件,跟顺序环视锚点在前面不同,它的锚点在要求后面。
否定顺序与否定逆序环视要求左右侧文本不能是什么
假设我们要匹配双引号内的内容,我们可以使用".*?"来匹配,也可以使用"[^"]*"来匹配,更可以使用否定顺序环视"((?!").)*"来匹配
模式修饰符
模式修饰词 (?modifier)
元字符 | 名称 | 含义 | 示例与注 |
---|---|---|---|
(?i) | 开启不区分大小写匹配 | 应用在子表达式中 | |
(?-i) | 关闭不区分大小写匹配 | 与(?i)配合使用 | |
\Q..\E | 文字文本范围 | 之间的字符全部当做文本,不解析为元字符 |
在Java中可以在编译Pattern时指定不区分大小写,grep也可以使用-i来启用,但这是针对正则表达式全局的设定,如果我们要对局部进行细微控制的话,就需要用到(?i)来指定不区分大小写了,譬如想匹配Petter,首字母不区分大小写,但是后续字母必须全是小写,我们就可以使用(?i)p(?-i)etter,这样就不会匹配到PETTER
平常遇到元字符我们可以使用反斜线来转义,但是如果遇到大段文本,里面包含好多元字符的话,有反斜线转义显得啰嗦,而且表达式也不清晰,可以用\Q..\E来限定,里面的字符不会被解析为元字符
其他元字符
元字符 | 名称 | 含义 |
---|---|---|
| | alternation | 匹配分隔的表达式 |
() | 括号 | 限定结构的范围,分组,捕获计数 |
(?:……) | 限定范围和分组,但不增加捕获计数 | |
\1,\2 | 反向引用 | 匹配之前第一、第二括号内表达式匹配的内容 |
(?<Name>) | 命名捕获 | 可以使用名称来获取分组内容group(Name),而不是group(1)方式 |
(?> ) | 固化分组 | 不会交还已经匹配的内容 |
(?(if-exp) then-exp | else-exp) | 条件判断 |
竖线是选择元字符,可以理解成程序中的或,左右两边可以是普通的正则表达式,譬如说前面的字符组[1-5],跟1|2|3|4|5在语义上是等价的,不过我们在实现的时候能用字符组还是用字符组,因为多选结构正则引擎在没有优化的情况是会一个个匹配然后回溯的,效率上不如字符组
括号可以用来限定范围,分组,捕获计数,这里范围表示可以将括号内的正则看做一个整体,计数元字符就可以对整体作用,分组表示括号内的正则表示一个组,并增加捕获计数,在后面可以使用\1 \2等方式来反向引用前面分组正则匹配到的内容。举个例子,想找到连续两个重复单词,我们就必须要知道前面单词是什么,这时候就可以使用反向引用了,可以简单写为(\w+)\s+\1,这里没有界定单词,但在平常我们使用已经够了,后面可以加上单词分界符更精确
(?:)结构跟括号作用基本一样除了不增加捕获计数,也就是说不能使用反向引用来引用括号里的匹配内容,因为使用带捕获的括号时,正则引擎需要记录括号里的捕获内容,回溯时也需要更改状态,如果只是为了分组,我们就可以使用它来减少引擎负担,加快速度
命名捕获相当于将括号内匹配的内容赋值给变量,后面我们不用\1方式来引用,而是可以直接使用name引用
固化分组,这个我们中字面上来理解就是已经匹配的内容固化,不会再吐出去让后面的表达式来匹配,使用固化分组可以使不符合条件的项尽快失败,减少重试次数。同时也会让引擎丢弃不需要保存的状态。
条件判断可以赋予我们分支执行的能力,目前用的不多
各种操作符的运算优先级
操作符 | 描述 |
---|---|
\ | 转义符 |
(), (?:), (?=), [] | 圆括号和方括号 |
*, +, ?, {n}, {n,}, {n,m} | 限定符 |
^, $, \anymetacharacter | 位置和顺序 |
| | “或”操作 |
参考文章:
正则表达式-基本概念与简单元字符 https://my.oschina.net/liufq/blog/1810099
正则表达式-锚点及模式修饰符 https://my.oschina.net/liufq/blog/1810642
https://www.php.cn/manual/view/32509.html
转载本站文章《深入正则表达式(2):元字符/关键子/符号说明》,
请注明出处:https://www.zhoulujun.cn/html/theory/algorithm/IntroductionAlgorithms/8427.html