版本比较
标识
- 该行被添加。
- 该行被删除。
- 格式已经改变。
参考:
正则表达式参考文档 - Regular Expression Syntax Reference
正则表达式概述
学习之前,可以暂时把正则表达式理解为功能更高级的通配符(wildcard)。
在Shell里,可以通过通配符来匹配一些具有相似模式的字符串,比如下面的命令:
代码块 |
---|
$ ls * # 列举所有的文件 $ ls *.c # 列举所有以".c"结尾的文件 $ ls 108?.c # 列举以108开头,后面跟一个任意字符,再以".c"结尾的文件 $ ls 108[56].c # 列举以108开头,后面跟5或6,再以".c"结尾的文件 $ ls 108[0-8].c # 列举以108开头,后面跟0~8中的任意一个,再以".c"结尾的文件 $ ls l08[!0].c # 列举以108开头,后面跟一个不为0的字符,再以".c"结尾的文件 |
其中,'*'、'?' 等就被称为通配符,因为他们可以匹配一个或多个符合规则的字符,具体的,这些通配符的作用如下:
- '*' 用于匹配零个或多个任意字符。
- '?' 用于匹配单个任意字符。
- '[]' 用于匹配可能出现的单个字符,比如[56]表示该位置可能出现5或6, 也可以使用范围的方式,比如[0-8]表示数字范围0~8,[a-i]表示字符范围a~i。
- '!' 用于排除单个字符。
通配符的作用就是用于字符串的匹配,但是在遇到比较复杂的匹配需求时,通配符就无能为力了,比如像“匹配以test开头,后面跟1个或多个数字,再跟3个非数字的字符,再以abcef中的一个或多个字符结尾的字符串”,或是匹配一个邮箱地址,只使用通配符的话就无法实现,这时,正则表达式就可以派上用场了。
正则表达式是比通配符功能更加强大的用于字符串模式匹配的工具,具体地,正则表达式可以实现以下功能:
- 字符串模式匹配,即验证字符串是否符合指定特征,比如验证一串字符是否是一个合法的邮箱地址;
- 在模式匹配的基础上,进行字符串分割,将字符串按规则分割成几个部分,以便于提取字符串,比如从邮箱地址中提取@前面的用户名;
使用正则表达式的最大问题是正则表达式有很多的流派(正则表达式“派别”简述 - Keep Coding),也就是在实现相同功能的情况下,不同流派的正则表达式可能有不同的写法。具体来说,正则表达式可以分为以下几类:
- POSIX基础正则表达式(basic regular expression, BRE)
- POSIX扩展正则表达式(extended regular expression, ERE)
- Perl兼容的正则表达式(Perl compatible reqular expression, PCRE)
关于这几个流派,PCRE一般用于编程语言,比如Perl, Boost.Regex, java.util.regex。BRE和ERE一般用于POSIX程序,比如sed, awk, grep等,其中sed默认使用BRE,awk默认使用ERE,而grep则可以通过选项选择使用哪种流派的正则表达式。关于这几个流派的正则表达式差异可以参考:正则表达式 BRE ERE PCRE 区别 | IT日记,本篇主要介绍PCRE。
学习正则表达式可以借助一些在线测试的网站,比如:在线正则表达式测试工具,Regexp在线调试 - 在线工具。正则表达式工作面板 - Regular Expression Workshop
正则表达式规则
关于模式匹配
所谓模式匹配,其实就是给出一个模式串和目标串,然后判断目标串中是否存在匹配串。
模式串用以描述要匹配的字符串特征,可以包含用于模式匹配的元字符,也可以包含普通字符。目标串则可以是任何字符串,匹配串是目标串中符合模式串的实际字符串。
以上面的通配符举例,abc[de]f
就是一个模式串,这个模式串描述的字符串特征是“abc开头,后面接一个d或e,再接一个f”
。目标串可以是任何字符串,这里以目标串abcdefg
举例,在这个目标串里,不存在与模式串匹配的目标串。把目标串改成abcdfg
,则存在一个匹配串abcdfg
,匹配位置是从1开始,结束于5(下标从1开始)。如果把目标串改成abcdfgabcefg
,则存在两个匹配串abcdfgabcefg
,第一个匹配串起始于1,结束于5,第二个匹配串起始于7,结束于11。
模式串不一定要匹配整串目标串,只要部分匹配,就算匹配成功,甚至在一个目标串里可以存在多个匹配串。
正则表达式充当的就是模式串的作用,通过正则表达式可以描述一个待匹配的字符串特征。
元字符
正则表达式其实就是一串用于描述字符串特征的字符串,包含普通字符和元字符。普通字符用于匹配目标串里的相同字符,比如正则表达式abc
,在匹配目标串abcdef
时,匹配的结果是成功,匹配到的内容是abc,匹配到的位置是开始于1,结束于3。元字符和Shell里的通配符功能类似,用于匹配一类字符,只不过正则表达式里的元字符数量比Shell里的通配符数量要多很多,功能也比通配符强大。下面的表格包含正则表达式里可用的元字符,其中有些是转义字符:
表达式 | 可匹配 |
---|---|
\r,\n | 匹配回车和换行符 |
\t | 匹配制表符 |
\\ | 匹配 '\' |
\^ | 匹配 ^ |
\$ | 匹配 $ |
\. | 匹配 . |
\xXX | 表示一个转义的ASCII字符,XX编号在0~255以内,比如\x20表示空格 |
\uXXXX | 表示一个转义的Unicode字符,XXXX是Unicode编码,通常用于校验CJK编码,比如\u4E00表示汉字“一” |
\d | 任意一个数字,0~9中的任意一个 |
\D | 匹配所有的非数字字符 |
\w | 任意一个字母、数字或下划线,也就是A~Z, a~z,0~9, _ 中任意一个 |
\W | 匹配字母、数字、下划线以外的字符 |
\s | 空格、制表符、换页符等空白字符中的任意一个 |
\S | 匹配所有非空字符 |
. | 匹配除换行符(\n)以外的任意一个字符 |
[ab5@] | 匹配'a'、'b'、'5'、'@'中的任意一个 |
[^abc] | 匹配'a'、'b'、'c'之外的任意一个字符 |
[f-k] | 匹配'f'~'k'之间的任意一个字符 |
[^A-F0-3] | 匹配'A'~'F','0'~'3'之外的任意一个字符 |
^ | 匹配字符串起始位置,不匹配任何实际字符 |
$ | 匹配字符串结束位置,不匹配任何实际字符 |
\b | 匹配一个单词边界,也就是单词和空格之间的位置,不匹配任何字符 |
\B | 匹配非单词边界,即左右两边都是"\w"范围或左右两都不是"\w"范围时的字符间隙 |
{n} | 表达式重复n次,比如 a{5} 相当于 aaaaa |
{m, n} | 表达式重复m~n次,比如 ba{1,3} 可以匹配 ba , baa , baaa |
{m,} | 表达式至少重复m次,比如 \w\d{2} 可以匹配 a12 , _456 , m1234 , ... |
? | 表达式出现0次或1次,相当于{0,1} ,比如 a[cd]? 可以匹配a , ac , ad |
+ | 表达式至少出现1次,相当于{1,} ,比如 a+b 可以匹配 ab , aab , aaab , ... |
* | 表达式不出现或出现任意次,相当于{0,} , 比如 \^*b 可以匹配 b , ^b , ^^b , ^^^b , ... |
| | 左右两边的表达式之间“或”的关系,匹配左边或右边。 |
() |
|
关于匹配次数中的贪婪与非贪婪
在基于出现次数进行匹配时,正则表达式总是基于可行的最大次数进行匹配,以目标串dxxxdxxxd
为例,下面两个正则表达式的匹配规则描述如表格所述:
表达式 | 匹配结果 |
---|---|
(d)(\w+) | \w+ 将匹配第一个"d"后面的所有字符"xxxdxxxd" |
(d)(\w+)(d) | \w+ 将匹配第一个"d"和最后一个"d"之间的全部内容,虽然 \w+ 可以连最后一个"d"也匹配上,但是为了让表达式匹配成功,\w+ 不得不让出最后一个可以匹配上的"d" |
上面的示例可以可以说明在基于次数进行匹配时,在保证可匹配成功的情况下,正则表达式总是尽可以多地匹配字符,这点对 {m,n}
, {m,}
, ?
, *
也一样,这种匹配原则叫作“贪婪”模式。
非贪婪模式:
在匹配次数的特殊符号后面加一个"?"号,可以使匹配次数不定的表达式尽可能少的匹配,使可匹配或不匹配的表达式,尽可能地不匹配。
表达式 | 匹配结果 |
---|---|
(d)(\w+?) | \w+? 将尽可能少得匹配"d"之后的字符,这里只会匹配第一个"x" |
(d)(\w+?)(d) | 为了让整个表达式区分成功,\w+? 将不得不匹配"d"后面的"xxx"才可以让后面的"d"匹配,结果是 \w+? 只匹配了"xxx" |
反向引用\1, \2...
在匹配过程中使用前面括号中已经匹配的结果,比如匹配配对的标签<td>aa</td>
或<p>aa</p>
,它的正则表达式写法如下:
<([^>]+)>.*?</\1>
上面的正则表达式中使用括号来记录当前位置所匹配的结果,对于目标串<td>aa</td>
及<p>aa</p>
来说,括号部分匹配的内容分别是td
和p
。括号部分匹配的内容可以继续用于正则表达式的匹配,引用方法是"\"
后面加上一个从1开始的数字,比如\1
表示第1个括号的内容,\2
表示第2个括号的内容。
预搜索与非获取匹配
表达式 | 匹配结果 |
---|---|
(pattern) | 匹配pattern并获取这一匹配的子字符串,该子字符串可用于向后引用,通过\1, \2的方式来引用 |
(?:pattern) | 匹配pattern但不获取匹配的子串,比如 "industr( ?:y|ies)" 可用于匹配 industry 和 industries ,等效于 "industry|industries" |
(?=pattern) | 正向肯定预搜索,要求匹配的字符串右侧必须符合pattern,但匹配的字符串本身不包含pattern,比如 "Windows(?=95|98|NT|2000)" 能匹配 "Windows2000" 中的"Windows" ,但不能匹配 "Windows3.1" 中的 "Windows" 。预搜索不消耗pattern字符,也就是说,下一次开始匹配的位置起始于上一次匹配的字符串之后,不包含pattern。比如 "Windows(?=2000|Windows)" 在匹配 "WindowsWindows2000" 时,第一次匹配的位置是"WindowsWindows2000" ,第二次匹配的位置是"WindowsWindows2000" 。 |
(?!pattern) | 正向否定预搜索,要求匹配的字符串右侧必须不符合pattern,比如 |
(?<=pattern) | 反向肯定预搜索,与正向肯定预搜索类似,只是方向相反。比如 "(?<=95|98|NT|2000)Windows" 可以匹配 "2000Windows" 中的 "Windows" ,但不能匹配 "3.1Windows" 中的 "Windows" 。 |
(?<!pattern) | 反向否定预搜索,与正向否定预搜索类似,只是方向相反。比如 "(?<!95|98|NT|2000)Windows" 可以匹配 "3.1Window" 中的 "Windows" ,但不能匹配 "2000Windows" 中的 "Windows" 。 |
匹配属性
正则表达式引擎的参数,用于修改正则表达式的匹配行为,有以下几个:
属性 | 说明 |
---|---|
忽略大小写 | 匹配时忽略大小写,默认区分大小写。 |
单行模式 | 默认"." 不匹配换行符(\n),配置为单行模式后,"." 可匹配换行符,也就是"." 可匹配任何字符。 |
多行模式 | 当目标串存在换行符(\n)时,"^" 和"$" 默认只匹配整串的开头和结尾,设置成多行模式后,"^" 和"$" 可以匹配每行的开头和结尾。 |
全局匹配 | 用于编程语言执行替换功能,替换所有的匹配串,默认只替换第一个。 |
C++中的正则表达式
// todo
sed中的正则表达式
// todo
awk中的正则表达式
// todo
grep中的正则表达式
// todo
正则示例
- 任何字符串:
.*
- 邮箱:
^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
- UUID:
[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}
,比如1f58bf32-5d5a-4a8e-8af4-24e3cc8fd406
。
正则使用记录
notepad++中替换markdown文件的图片链接,把形如  的链接替换成 ,操作如下:
正则表达式:
!\[Alt\stext\]\(\.\/(\d+.png)\)
!\[Alt text\]\($1\)
目录 |
---|