本文
主要对正则表达式的常用语法进行整理和介绍。
版本 | 说明 |
---|---|
0.1 | 初版发布 |
0.2 | 添加正则表达式在线测试网址 |
背景
- Linux
什么是正则表达式?
正则表达式(regular expression 简称regex),正则表达式是一种文本处理工具,用途就两种:一是进行搜索,二是进行搜索和替换。但是它比普通的搜索更强大,具体可以在如下场景中体现:
- 在某文本中搜索或搜索加替换单词car,但是并不将carry等包含car字符的单词作为目标
- 在某文本中搜索或搜索加替换单词car,但是只将行末位置作为目标
- 在某文本中搜索或搜索加替换begin****end,这里将begin和end以及中间任意字符作为目标
总之,正则表达式就是一个文本处理功能强大的工具,主要任务就是用正则表达式语言构造特定的目标来进行搜索和替换操作。但是它又不能算作一个独立的工具或语言,二是嵌入在其他语言和工具中,换句话说,其他大多数工具和语言都支持正则表达式。
正则表达式知识点
任意字符“.”
- “.”字符可以匹配任何单个字符、字母、数字以及“.”字符本身。
- “.”字符可以在同一正则表达式在任意位置出现任意次数。
- 如果想搜索“.”字符本身,需要自“.”字符前加“\”字符进行转义,即“\.”。
字符集合[]
- [ns]a 匹配na和sa,将部分字符集合作为目标
- [Rr]eg 匹配Reg和reg,特定位置区分大小写和不区分大小写
- [0-9] 匹配任意数字,这里“-”为连字符(仅限于字符集合[]内,字符集合外为普通字符-),常用为0-9,A-Z,a-z
- [A-Za-z0-9] 匹配任意大小写字母和数字,字符集合中可以出现多个区间
- [^0-9] 匹配非数字任意字符,取非字符“^”,主要取非的含义是对整个字符集合,而不是^后紧跟的那个集合
使用元字符
- 特殊字符需要转义,如\\匹配\,\.匹配.,\[匹配[
- 匹配空白字符,\f换页符,\n换行符,\r回车符,\t制表符(如匹配空白行,linux下将换行符作为文本结束标志,而windows将回车符换行符作为文本结束标志,所以linux匹配空白行即\n\n,windows下陪陪空白行即\r\t\r\t)
- 匹配某类字符,\d匹配任何数字字符,\D匹配任意非数字字符,\w匹配任意字母数字下划线字符(不区分大小写),\W匹配任意非字母数字下划线
- 匹配空白字符,\s匹配任意空白字符(等同于[\f\n\r\t\v]),\S匹配任意空白字符(等同于[^\f\n\r\t\v])
重复匹配
- [0-9]或\d代表任意一个数字,[0-9]+或\d+代表任意一个或多个连续数字
- +和*和?的使用区别,+代表重复出现一次或多次,*代表重复出现0次或多次,?代表重复出现0次或1次。举例:https+匹配https和httpss…,https*匹配http和https和httpss…,https?匹配http和https。
- 设定重复次数,a{5}匹配aaaaa,重复5次;a{3,5}匹配aaa和aaaa和aaaaa,重复3-5次;a{3,}匹配a重复最少3次
- 防止过度匹配:+,*,{3,}这样的无限重复都属于贪婪型,容易造成过度匹配,举例如下,AxxxB 555 AmmmmB,正则表达式为A.*B,本意是将AxxxB和AmmmmB匹配,实际匹配为AxxxB 555 AmmmmB,显然不是我们希望的结果,在贪婪型元字符+和*和{3,}后加?可以将贪婪型转为懒惰型,如A.*?B,匹配结果为AxxxB和AmmmmB。
位置匹配
- 单词边界符\b:例如匹配car,可能将carry也作为了目标,这时候需要使用单词边界,\bcar\b。这里对\b的匹配说明一下,他并不知道什么是单词边界,只是找到构成单词的字符(字母数字下划线\w)和一个不能构成单词的字符(\W)之间位置。
- 字符串的边界:开头^,结尾$,比如^.*$,在语法上完全正确,而且总能匹配到一个结果,但是却无任何用途。这里字符串边界符要搭配分行匹配模式(?m)来使用的,举例说明:匹配verilog代码中的注释行内容:(?m)^\s*//.*$,解释:(?m)分行匹配模式,将一行内容作为一个字符串处理;^字符串开头;\s*任意一个或多个空白符,如空格、制表符、换行符等等;//为verilog中的注释符;.*为任意字符重复0次或多次,其实这里代表注释内容;$代表字符串结尾。
- 总之,比较难理解的是字符串边界的使用,这里对常用的几个举例:
- ^\s*:代表文本开头的有效内容处(这里有效内容是排除空行、空格、tab等)
- \s*$:代表文本末尾的有效内容处(这里有效内容是排除空行、空格、tab等)
- 搭配(?m)作为行处理:(?m)^\s*代表行首有效内容;(?m)\s*$代表行尾有效内容
子表达式
- 什么是子表达式?举一个简单例子,as{2}匹配的内容是ass,因为{2}是以紧跟自己上一个字符作为操作对象的,而当你想匹配asas时,需要写为(as){2},这里括号里内容就是子表达式,括号就是子表达式的标志,这样{2}以子表达式内容为操作对象。
- 另一举例:文本内容为1965,匹配年份数字,正则表达式为19|20\d{2},实际匹配到内容为19,而本意是将19xx和20xx的年份数字匹配出来,问题出在|操作,这里|或操作是将符号两边都作为整体来处理,即19和20\d{2},所以这里需要子表达式将19|20作为整体,即(19|20)\d{2}
- 子表达式的嵌套,这里对于子表达式的嵌套没什么好讲的,就是支持无限嵌套,但是也要适可而止,以便于阅读分析。这里举个例子:匹配三个连续0-255的数字(注意,正则表达式不会有任何计算,它只关心字符),正则表达式为:((\d{1,2})|(1\d{1,2})|(2[0-4]\d)|(25[0-5])){3};这里就是将0-99,100-199,200-249,250-255分段描述。
回溯引用
- 回溯引用其实就是后面匹配引用前面匹配的结果,简单的就是前后一致匹配。例如:
[ ]+(\w+)+[ ]\1
,解释为[ ]+
代表任意一个或多个空格;(\w+)
代表任意一个或多个数字字母下划线;[ ]+
代表任意一个或多个空格;\1
为前面子表达式(\w+)
匹配的内容,这里就是回溯引用。这段表达式实际功能就是在匹配两个连续单词的出现,如and and,we we等等。 - 回溯引用通过
\1
,\2
等来表达的,这里数字是通过相对位置来引用的,即此处\1
,代表表达式中第一个子表达式(嵌套表达式是如何暂不清楚,认为是指引用层级相同的子表达式),注意数字从1开始。 - 回溯引用在替换中的应用:
- 将电话号码重新排版,将313-555-1234和251-555-2031排版为(313) 555-1234和(251) 555-2031。匹配表达式:
(\d{3})(-)(5{3})(-)(\d{4})
,替换表达式:($1) $3-$5
。 - 大小写转换:元字符(\E作为结束标志;\l把下一个字符转换为小写;\u把下一个字符转换为大写;)举例:AaBbCcDd,匹配:
([Aa]{2})([Bb]{2})([Cc]{2})([Dd]{2})
,替换:$1\U$2$3\E$4
,替换效果为AaBBCCDd(将第二第三表达式全部替换为大写)
- 将电话号码重新排版,将313-555-1234和251-555-2031排版为(313) 555-1234和(251) 555-2031。匹配表达式:
前后查找
- 什么是前后查找?前后查找就是将匹配部分作为位置标记,选择前面内容还是后面内容,而作为位置标记的内容不作为匹配目标或者说不做内容提取。
- 向前查找?=,向后查找?<=,作为查找位置的匹配写在=后面,且必须作为子表达式,换句话说必须括起来,如(?=:)。
- 举例:http:xxxx和https:ssss,匹配:.+(?=:),匹配结果http和https,注意无:本身。
- 举例:001:$0.23和002:\(1.54,匹配:(?<=\))[\d.]+,匹配结果0.23和1.54,注意无$本身。向后匹配要注意内容的匹配条件,不要过多匹配,其实用的最多还是向前匹配,以及向前向后匹配合用。
- 举例:begin 12345678 end,匹配(?<=begin).+(?=end),匹配结果 12345678 ,注意无begin和end。
- 之前描述的向前向后查找被称为正向前查找和正向后查找,除此外,还有负向前查找(?!)和负向后查找(?<!),用法相同,只是将非匹配位置作为搜索条件,注意,对于使用负向前和符负向后查找时,可以适当加单词边界符\b。举例:\(30 for 10 apples,匹配:(?<!\))\d+,匹配结果为$30 for 10 apples,因为$30中的0并不是\(开头,所以会作为匹配目标,只需加字符边界符\b就可以解决:\b(?<!\))\d+\b。
嵌入条件
- 嵌入条件使用?来表示,在两种情况下使用:一是根据一个回溯引用来进行条件处理;二是根据一个前后查找来进行条件处理。
- 回溯引用使用嵌入条件,举例:123-456-7890和(123)456-7890,匹配:(\()?\d{3}(?(1)\)|-)\d{3}-\d{4},解释:(\()?是一个可选(?为0或1个)的左括号(;\d{3}是区号123;(?(1)\)|-)是引用条件第一个子表达式成立则匹配右括号),否则必须匹配-符号;\d{3}-\d{4}是后面7位号码。
- 前后查找条件的嵌入条件,举例:11111和44444-444,匹配:\d{5}(?(?=-)-\d{4}),解释:\d{5}表示前五位数字;(?(?=-)-\d{4}向前查找是否存在-,如果存在-再后续查找-\d{4}也就是连字符-和三个数字。
- 总之,嵌入条件的模式还是很复杂的,建议先对该模式的各个组成部分一一调试,然后再组合到一起,还有,工作中使用到如此复杂的情况还是比较少见的,建议还是用更简单的方法实现同样的目的。
元字符列表
注意:因为org-mode下列表中“|”符号为特殊符号,为了防止描述真实的“|”符号时被转义,文中使用中文 “竖线” 替代。
基本元字符
元字符 | 说明 |
---|---|
. | 匹配任意单个字符 |
竖线 | 逻辑或操作符 |
[] | 匹配字符集合中的任意一个字符 |
[^] | 对字符集合取非 |
- | 定义一个区间,如A-Z |
\ | 对下一个字符转义 |
数量元字符
元字符 | 说明 |
---|---|
* | 匹配前一个字符或子表达式零次或多次重复 |
*? | *的懒惰型,懒惰型的理解可参考第五节内容重复匹配 |
+ | 匹配前一个字符或子表达式一次或多次重复 |
+? | +的懒惰型 |
? | 匹配前一个字符或表达式重复一次或零次(可以理解为有无该字符) |
{n} | 匹配前一个字符或表达式重复n次,n为具体数值 |
{n,m} | 匹配前一个字符或表达式重复m次至n次 |
{n,} | 匹配前一个字符或表达式重复至少n次 |
{n,}? | {n,}的懒惰型 |
特殊字符元字符
元字符 | 说明 |
---|---|
[\b] | 匹配一个退格字符 |
\c | 匹配一个控制字符 |
\d | 匹配任意数字字符 |
\D | \d的反义 |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\s | 匹配一个空白字符 |
\§ | \s的反义(大写S) |
\t | 制表符tab |
\v | 垂直制表符 |
\w | 任意数字字母下划线 |
\W | \w的反义 |
\x | 匹配一个十六进制数 |
\0 | 匹配一个八进制数 |
回溯引用和前后查找
元字符 | 说明 |
---|---|
() | 定义子表达式 |
\1 | 匹配第一个子表达式,若是\2表示匹配第二个子表达式 |
?= | 向前查找 |
?<= | 向后查找 |
?! | 负向前查找 |
?<! | 负向后查找 |
?() | 条件if then |
?()加竖线 | 条件if then else |
大小写转换
元字符 | 说明 |
---|---|
\E | 结束\L或\U转换标志 |
\l | 把下面一个字符转义为小写 |
\L | 把下面字符转义为小写直至\E |
\u | 把下面一个字符转义为大写 |
\U | 把下面字符转义为大写直至\E |
匹配模式
元字符 | 说明 |
---|---|
(?m) | 分行匹配模式 |
其他说明
使用正则表达式的难点不在于将一个匹配情况考虑清楚并且写出一个符合要求的正则表达式,难点在于将不需要匹配的情况考虑清楚将其排除在外。
在线测试
典型应用
待补充。
文章原创,可能存在部分错误,欢迎指正,联系邮箱 cao_arvin@163.com。