正则表达式
概述
正则表达式,Regular Expression,缩写为 regex、regexp、RE等。
正则表达式是文本处理极为重要的技术,用它可以对字符串按照某种规则进行检索、替换。
1970年代,Unix 之父 ken Thompson 将正则表达式引入到 Unix 中文本编辑器 ed 和 grep 命令中,由此正则表达式普及开来。
1980年后,per 语言对 Henry Spencer 编写的库,扩展了很多新的特性。1997年开始,Philip Hazel 开发出了 PCRE(Perl Compatible Regular Expressions),它被 PHP 和 HTTPD 等工具采用。
正则表达式应用极其广泛,shell 中处理文本的命令、各种高级编程语言都支持正则表达式。
正则表达式的元字符
代码 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\s | 匹配任意的空白字符 |
\d | 匹配数字 |
\b | 匹配单词的开始或结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
正则表达式的转义
有时候在我们查找 .
,或者 *
,或者一些元字符本身的话,会有一些问题出现,因为这些元字符已经变成别的意思了,所有我们没有办法指定这些元字符。出现这种情况,我们就得使用 \
来取消这些字符的特殊意义。所有,我们应该使用 \.
和 \*
。当然,要查找 \
本身,你也得用 \\
- 例如:
deerchao\.net
匹配 deerchao.net
C:\\Windows
匹配 C:\Windows
正则表达式的重复
正则表达式中的限定符(指数量的代码,例如*, {5,12} 等):
代码 / 语法 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复 n 次 |
{n,} | 重复 n 次或更多次 |
{n,m} | 重复 n 到 m 次 |
下面是一些使用的例子:
Windows\d+
匹配 Windows 后面跟 1 个或者更多数字
^\w+
匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思还得看选项设置)
正则表达式的分枝条件
先来看几个例子:
0\d{2}-\d{8}|0\d{3}-\d{7}
这个表达式匹配两种以连字符分割的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是四位区号,7位本地号(如0376-2233445)
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}
这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字符或空格间隔,也可以没有间隔。
\d{5}-\d{4}|\d{5}
这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字符间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分支条件时,要注意各个条件的顺序。
如果你把它改成 \d{5}|\d{5}-\d{4}
的话,那么就只会匹配5位的右边(以及9位右边的前5位)。原因是匹配分支条件时,将会从左到右地测试每一个条件,如果满足了某个分支的话,就不会再去管其他条件了。
正则表达式之分组
在字符后面加上限定字符就可以重复单个字符,那么多个字符的重复又该如何实现呢?
可以使用小括号来指定子表达式(也称分组),然后对于这个字表达的重复次数你就可以自行规定了,子表达式也可以进行一些其他的操作。
(\d{1,3}\.){3}\d{1,3}
这是一个 简单的IP地址匹配表达式。第一步分析\d{1,3}
匹配到1到3位的数字,第二步(\d{1,3}\.){3}
匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字\d{1,3}
不过上面的表达式并不完整,会把 256.666.777.999 这种不存在的IP地址也匹配到,如果能用算数比较的话,或许能够简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]|25[0-5]|[01]?\d\d?)
正则表达式之反义
有时候需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其他任意字符都行的情况,这时需要用到 反义
:
代码 / 语法 | 说明 |
---|---|
\W | 匹配任意不是字符,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白字符的字符 |
\D | 匹配任意非数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^x] | 匹配除 x 以为的任意字符 |
[^aeiou] | 匹配除了 aeiou 这几个字母以外的任意字符 |
例子:
\S+
匹配不包含空白符的字符串
<a[^>]+>
匹配用尖括号括起来的以 a 开头的字符串
正则表达式之向后引用
当我们用小括号指定一个子表达式之后,就要对这个子表达式的文本进行匹配,即此分组捕获的内容,可以在表达式或其他程序中作进一步的处理。一般情况下,每个分组都会自动拥有一个组号,它的规则是:从左到右以分组的左括号作为标志,把第一次出现的分组的组号定为1,第二个即2,以此类推下去。
向后引用
用于重复搜索前面某个分组匹配的文本。例如,\1
代表分组1匹配的文本。我们根据示例来深刻理解:
\b(\w+)\b\s+\1\b
可以用来匹配重复的单词,像 go go,或者 python python。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字 \b(\w+)\b
,这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符 \s+
,最后是分组1中捕获的内容(也就是前面匹配的那个单词)\1
。
你也可以自己制定子表达式的 组名
。要指定一个分组的组名,请使用这样的语法:(<word>\w+)
(或者把尖括号换成 ’ 也行):(?'word'\w+)
,这样就把 \w+
的组名指定为 word
了。要反向引用这个分组 __捕获__的内容,你可以使用 \k<word>
,所以上一个例子也可以写成这样:\b(<?Word>\w+)\b\s+\k<Word>\b
使用小括号的时候,还有很多特定用途的语法,下面列出了最常用的一些:
正则表达式之零宽断言
(?=exp)
也叫零宽度正预测先行断言,它断言自身出现的位置后面能匹配表达式 exp 。比如\b\w+(?=ing\b)
,匹配以 ing 结尾的单词的前面部分(除了ing以外的部分),如查找 I’m singing while you’re dacing 时,它会匹配 sing 和 danc。
(?<=exp)
也叫零宽度正回顾后发断言,它断言自身出现的位置的签名能匹配表达式 exp。比如 (?<=\bre)\w+\b
会匹配以 re 开头的单词的后半部分(除了re以外的部分),例如在查找 reading a book 时,它匹配 ading。
假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分: ((?<=\d)\d{3})+\b
,用它对 1234567890 进行查找时结果是 23456890。
下面这个例子同时使用了这两种断言:
(?<=\s)\d+(?=\s)
匹配以空白符间隔的数字(再次强调,不包括这些空白符)。
注解:
- 断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。