正则表达式
使用工具
简介
字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
正则表达式是由普通字符(例如字符 a 到 z)以及特殊字符(称为"元字符")组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
工具
正则表达式其实是很难复杂理解的,就算很熟悉的人也容易写错,所以需要一些正则表达式的测试工具,来测试你的正则表达式是否书写正则,这些工具既有在线版又有客户端版。
我个人在Mac上用的是Regex101
,在线版,客户端。Regex101
支持PHP、JavaScript、python、golang。
元字符
字符匹配
元字符 |
说明 |
. |
匹配除换行符以外的任意字符 |
\w |
匹配字母或数字或下划线或汉字 |
\s |
匹配任意的空白符 |
\d |
匹配数字 |
\b |
匹配单词的开始或结束 |
^ |
匹配字符串的开始 |
$ |
匹配字符串的结束 |
反义
有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义。
元字符 |
说明 |
\W |
匹配任意不是字母,数字,下划线,汉字的字符 |
\S |
匹配任意不是空白符的字符 |
\D |
匹配任意非数字的字符 |
\B |
匹配不是单词开头或结束的位置 |
[^x] |
匹配除了x以外的任意字符 |
[^aeiou] |
匹配除了aeiou这几个字母以外的任意字符 |
字符转义
如果你想查找元字符本身的话,比如你查找.
或者*
就出现了问题,你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用转义符\
来取消这些字符的特殊意义。因此,你应该使用\.
和\*
。当然,要查找\本身,你也得用\\
。
字符限定(重复次数 贪婪和懒惰)
元字符 |
说明(贪婪) |
* |
重复零次或更多次 |
+ |
重复一次或更多次 |
? |
重复零次或一次 |
{n} |
重复n次 |
{n,} |
重复n次或更多次 |
{n,m} |
重复n到m次 |
元字符 |
说明(懒惰) |
*? |
重复零次或更多次,但尽可能少重复 |
+? |
重复一次或更多次,但尽可能少重复 |
?? |
重复零次或一次,但尽可能少重复 |
{n,}? |
重复n次或更多次,但尽可能少重复 |
{n,m}? |
重复n到m次,但尽可能少重复 |
分组()
元字符 |
说明 |
() |
用小括号来指定子表达式 (也叫分组 ) |
我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式
(也叫做分组
)。
字符类
元字符 |
说明 |
[] |
用小括号来指定子表达式 (也叫分组 ) |
要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?
很简单,你只需要在方括号里列出它们就行了,像[aeiou]
就匹配任何一个英文元音字母,[.?!]
匹配标点符号(.或?或!)。
简单的说上面的字符匹配里介绍的那些元字符已经定义好的一些字符匹配的类,而这里我们用[]
来自定义一个字符集的类。
分枝条件
元字符 |
说明 |
| |
相当于或 ,只要满足其中一种规则就算匹配 |
零宽断言
元字符 |
说明 |
(?=exp) |
匹配exp前面的位置 |
(?<=exp) |
匹配exp后面的位置 |
用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。
负向零宽断言
元字符 |
说明 |
(?!exp) |
匹配后面跟的不是exp的位置 |
(?<!exp) |
匹配前面不是exp的位置 |
前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u。
注释
元字符 |
说明 |
(?#comment) |
这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 |
示例
字符匹配
规则 |
说明 |
测试 |
\ba\w*\b |
匹配以字母a开头的单词 |
learn more about a Podspec see a123_4 |
\d+ |
匹配1个或更多连续的数字 |
1234:是几个数字 110:是报警电话 6666 |
\b\w{6}\b |
匹配刚好6个字符的单词 |
iueuyy 666 sn_nks ksj jdf9_l |
^\d{5,12}$ |
匹配整个字符串必须是5至12位的数字 |
正确:1234567 错误:a1234567 |
反义
规则 |
说明 |
测试 |
\ba\W*\b |
匹配以字母a开头的非字母,数字,下划线,汉字的字符串 |
learn more about a*)Podspec a. see a123_4 |
\D+ |
匹配1个或更多连续的非数字 |
1234:是几个数字 110:是报警电话 6666 |
\B\W{6}\B |
匹配不是单词开头或结尾的六个字符的非字母,数字,下划线,汉字的字符串 |
i***72 (****\ (**8*\ |
^\D{5,12}$ |
匹配整个字符串必须是5至12位的非数字 |
正确:a_*(efg 错误:1_*(efg |
<a[^>]+> |
匹配用尖括号括起来的以a开头的字符串 |
string is <ahu& 66eu> |
字符转义
字符限定(重复次数 贪婪和懒惰)
规则 |
说明 |
测试 |
Windows\d+ |
匹配Windows后面跟1个或更多数字 |
string Windows23421one |
^\w+ |
匹配一行的第一个单词或整个字符串的第一个单词 |
正确:s_8 string 错误:(s_8 string |
当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b
,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab
的话,它会匹配整个字符串aabab
。这被称为贪婪匹配。
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?
就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。如刚才的例子aabab
,使用a.*?b
,则会匹配aab
和ab
。
贪婪 |
例子 |
匹配结果 |
a.*b |
aabab |
aabab |
懒惰 |
例子 |
匹配结果 |
a.*?b |
aabab |
aab 和ab |
分组()
规则 |
说明 |
测试 |
(\d{1,3}\.){3}\d{1,3} |
简单的IP地址匹配表达式 |
my ip is 192.168.137.66 |
字符类
规则 |
说明 |
测试 |
\(?0\d{2}[)-]?\d{8} |
匹配几种格式的电话号码 |
(010)88886666 或 022-22334455 或 02912345678 |
[]
也就是自定义自己定义的字符类,比如[0-9]
等于 \d
,而[0-8]
则匹配除了9以外的所有数字。
条件分支
规则 |
说明 |
测试 |
\d{5}-\d{4}|\d{5} |
匹配美国的邮政编码 |
code is 56321-6123 next is 12345 |
这个相当于或
语句,按照|
来分割多个规则,只要满足其中一个就算匹配。
要注意的是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。所以如果把上面的那个例子写成\d{5}|\d{5}-\d{4}
,那么就会配置错误,匹配出56321
和12345
。
所以写分枝条件时,从左到右这个规则应该从严格到宽松。
零宽断言
规则 |
说明 |
测试 |
\b\w+(?=ing\b) |
匹配以ing结尾的单词的前面部分(除了ing以外的部分) |
I'm singing while you're dancing |
(?<=\bre)\w+\b |
匹配以re开头的单词的后半部分(除了re以外的部分) |
reading a book |
负向零宽断言
规则 |
说明 |
测试 |
\b\w*q(?!u)\w*\b |
匹配包含后面不是字母u的字母q的单词 |
Iraq fighting |
(?<![a-z])\d{7} |
匹配前面不是小写字母的七位数字 |
A1234567 |
这里的第一个例子你可能刚开始会想到用反义来解决,如\b\w*q[^u]\w*\b
,但是你会发现它是连空格也匹配进去了,也就是[^u]
反义的时候包含了空格等元素。
而负向零宽断言为什么能不匹配到空格呢?因为它只匹配一个位置,并不消费任何字符,所以它才叫零宽
。
注释
规则 |
说明 |
测试 |
2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199) |
() 里的表达式不对正则产生影响,只是注释了说这个条件分支的正则表达式匹配的范围是200-249或250-255或0-199 |
200 300 156 |
处理选项
名称 |
说明 |
IgnoreCase(忽略大小写) |
匹配时不区分大小写 |
Multiline(多行模式) |
更改^ 和$ 的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$ 的精确含意是:匹配\n 之前的位置以及字符串结束前的位置.) |
Singleline(单行模式) |
更改. 的含义,使它与每一个字符匹配(包括换行符\n ) |
IgnorePatternWhitespace(忽略空白) |
忽略表达式中的非转义空白并启用由#标记的注释 |
ExplicitCapture(显式捕获) |
仅捕获已被显式命名的组 |
Python中使用正则表达式
个人喜欢用Python来写脚本,所以这里用Python中使用正则表达式来做示例,详情参考Python正则表达式。
参考
正则表达式30分钟入门教程