需要许多行语句才能完成的任务,基本上只需要一个正则表达式,一条语句就可以完成。正因为如此,不少人虽然认为正则表达式不够花哨、漂亮,却不得不承认它是一种“匕首应用”——匕首,没有十八般兵刃那么大方,关键时候却不可或缺,所以值得花时间练练。

正则表达式发源于与计算机密切相关的两个领域:计算理论和形式语言。

UNIX 下的文本编辑器ed最终演化为 grep(grep 得名自 ed 编辑器中的正则表达式搜索命令 g/re/p,其中 re 表示“正则表达式”)

正则表达式可以极大地提高代码的重用度和执行效率。如果完全不使用正则表达式,代码量会增加数倍甚至十倍。

除了 web 开发领域,需要实现大量自动化功能的一些领域,例如运维领域和自动化测试领域,也是正则表达式大显身手的地方。

“面向对象”是“广义相对论”,而“正则表达式”则是“量子力学”

经常需要处理文本的程序员自然会知道正则表达式的价值。

第一部分

第一章 字符组(character set)

字符组就是一组字符,在正则表达式中,表示“在同一个位置可能出现的各种字符”,其写法是在一对方括号[ 和 ] 之间列出所有可能出现的字符,简单的字符组比如 [ab]、[314]、[#.?]

  1. .NET C#
  2. // 匹配:true 不匹配:false
  3. Regex.IsMatch(charStr, "[0123456789]");
  4. Java
  5. // 匹配:true 不匹配:false
  6. charStr.matches("[0123456789]");
  7. JavaScript
  8. // 匹配:true 不匹配:false
  9. /[0123456789]/.test(charStr);
  10. PHP
  11. // 匹配:1 不匹配:0
  12. preg_match("/[0123456789]/", charStr);
  13. Python
  14. # 匹配:RegexObject 不匹配:None
  15. re.search("[0123456789]", charStr)
  16. Ruby
  17. # 匹配:0 不匹配:nil
  18. charStr =~ /[0123456789]/
  1. # 导入正则表达式对应的包
  2. import re
  3. re.search(pattern, string)

^:定位到字符串的起始位置
$:定位到字符串的结束位置

字符组中的字符排列顺序并不影响字符组的功能,出现重复字符也不会影响
范围表示法-表示范围,一般是根据字符对应的码值来确定的,码值小的字符在前,码值大的字符在后。
码值:0~9 是48~57;A~Z是65~90;a~z是97~122;
image.png

转义序列**\x**``_hex_ 表示一个字符,**\x**是固定前缀,表示转义序列的开头,num 是字符对应的码值(Code Point)

可以表示一些难以输入或者难以显示的字符,

元字符-``[``]``^``$ 在匹配中有特殊的意义。但是有时候并不需要表示这些特殊的意义,就必须通过转义来实现

-:如果紧邻字符组中的开方括号[,那么它就是普通字符,其它情况下都是元字符

正则表达式中的每个反斜线字符\,必须转义成 \\。例如[0\-9]需要表示成[0\\-9]

原生字符串(Row String):正则表达式是怎样,原生字符串就是怎样,完全不需要考虑正则表达式之外的转义(只有双引号字符是例外,原生字符串内的双引号字符必须转义写成\"。表达形式:r"string"

  1. r"^[0\-9]$" == "^[0\\-9]$ # true

只希望匹配字符串[012],写成[012]会被识别成字符组,因此需要转义[012],注意只有开方括号需要转义,闭方括号不需要转义。

排除型字符组(Negated Character Class):在当前位置,匹配一个没有列出的字符。表达式:[^...]

  • 排除型字符组必须匹配一个字符,即“在当前位置,匹配一个没有列出的字符”,而不是“在当前位置不要匹配列出的字符,即使不出现任何字符也可以”
  • ^仅在它紧跟在{之后时是一个元字符,如果不让它紧挨着,就表示“这个字符组中可以出现^字符”

字符组简记法(shorthands)

shorthands 含义 等价于正则表达式 备注
\d digit(数字字符) [0-9]
\w word(单词字符) [0-9a-zA-Z_] 特别注意还包括下划线_
\s space(空白字符) [ \t\r\n\v\f] 空格字符、制表符、回车符、换行符
\d\D 任意字符
\w\W 任意字符
\s\S 任意字符
\n 换行符
. 匹配除换行符以外的任意字符
\b 表示单词的开头或结尾,也就是单词的分界处 匹配的是位置
\B 匹配不是单词开头或结束的位置
[^x] 匹配除了x以外的任意字符
\S+ 不包含空白符的字符串
^ 匹配的是位置
$ 匹配的是位置

流派:
PCRE: Per Compatible Regular Expression
POSIX: Portable Operating System Interface for unix

第二章 量词

{m,n}: m 是下限,n是上限,均为闭区间。如果不确定长度的上限,可以省略,只指定下限,写成\d{m,}。量词限定次数一般都有明确下限,如果没有,则默认为0.最好使用{0,n}

量词简记法:

常用量词 {m,n}等价形式 说明
* {0,} 可能出现,也可能不出现,出现次数没有上限
+ {1,} 至少出现1次,出现次数没有上限
{0,1} 至多出现1次,也可能不出现

分支条件

使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

分组

用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作

  1. ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) # 匹配 IP 地址

每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

常用分组语法

分类 语法 说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里
(?exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释 (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

反向引用

用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本

贪婪与懒惰

贪婪:匹配尽可能多的字符。
懒惰:匹配尽可能少的字符。在限定符后面添加一个?
正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权

修饰符

  • u 修饰符:用于处理 4 个字节的 UTF-16 编码。点(.)字符可以匹配除换行符以外的任意单个字符(对于码点大于0xFFFF的Unicode字符,必须加上u才能识别)。
  • y 修饰符:y 修饰符与 g 修饰符类似,也是全局匹配,后一次匹配都是从上一次匹配成功的下一个位置开始。不同之处在于,g 修饰符只要剩余位置中存在匹配就行,而 y 修饰符会确保匹配必须从剩余的第一个位置开始,即“sticky”
  • s 修饰符:忽略行终止符,匹配任意单个字符。比点(.)字符匹配范围广,因此称作dotAll模式
  • m修饰符:多行修饰符

ES6 RegExp 的属性

  • sticky:是否设置了 y 修饰符
  • dotAll: 是否设置了 s 修饰符

    经典匹配

    | 正则表达式 | <(\S?)[^>]>.?</\1>|<.? /> | | —- | —- | | 匹配 | hello|abcd | | 不匹配 | abc|123|ddd |
正则表达式 <(\S?)[^>]>.?</\1>|<.? />
匹配 <html>hello</html>|<a>abcd</a>
不匹配 abc|123|<html>ddd
替换文本 <$1>$3<$4>
替换结果 hello|abcd
正则表达式 ^[^<>`~!/@\#}$%:;)(_^{&*=|’+]+$
匹配 This is a test
不匹配 | That’s it
正则表达式 <!—.*?—>
匹配 <!— <h1>this text has been removed</h1> —> | <!— yada —>
不匹配 <h1>this text has not been removed</h1>
正则表达式 (\[(\w+)\s(([\w])=(‘|")?([a-zA-Z0-9|:|\/|=|-|.|\?|&])(\5)?)\])([a-zA-Z0-9|:|\/|=|-|.|\?|&|\s]+)(\[\/\2\])
匹配 [link url="http://www.domain.com/file.extension?getvar=value&secondvar=value"]Link[/li
不匹配 [a]whatever[/b] | [a var1=something var2=somethingelse]whatever[/a] | [a]whatever[a]
正则表达式 href=\“\‘?\w+(\.\w+)(\/\w+(\.\w+)?)(\/|\?\w=\w(&\w=\w)*)?[\“\‘]
匹配 href=”www.yahoo.com” | href=”http://localhost/blah/“ | href=”eek”
不匹配 href=”” | href=eek | href=”bad example”
正则表达式 "(^"*)"
匹配 "This is a \"string\"."
不匹配 "This is a \"string\".
正则表达式 (?i:on(blur|c(hange|lick)|dblclick|focus|keypress|(key|mouse)(down|up)|(un)?load|mouse(move|o(ut|ver))|reset|s(elect|ubmit)))
匹配
不匹配 click | onandon | mickeymouse
正则表达式 (?s)/\.\*/
匹配 / ……………….. / | / imagine lots of lines here /
不匹配 / malformed opening tag / | / malformed closing tag /
正则表达式 <(\S?) [^>]>.?</\1>|<.? />
匹配 hello|abcd
不匹配 abc|123|ddd
正则表达式 \xA9
匹配 ©
不匹配 anything
正则表达式 src[^>]*[^/].(?:jpg|bmp|gif)(?:\"|\‘)
匹配 src="../images/image.jpg" | src="http://domain.com/images/image.jpg&quot; | src=’d:\w
不匹配 src="../images/image.tif" | src="cid:value"
正则表达式 /\[\d\D]?\*/
匹配 / my comment / | / my multiline comment / | / my nested comment /
不匹配 / anything here / | anything between 2 seperate comments | \ \
正则表达式 <[a-zA-Z]+(\s+[a-zA-Z]+\s=\s(“([^”])”|’([^’])’))\s/>
匹配 《正则指引》 - 图2
不匹配 《正则指引》 - 图3 | 《正则指引》 - 图4

一、校验数字的表达式

1 数字:^[0-9]$
2 n位的数字:^\d{n}$
3 至少n位的数字:^\d{n,}$
4 m-n位的数字:^\d{m,n}$
5 零和非零开头的数字:^(0|[1-9][0-9]
)$
6 非零开头的最多带两位小数的数字:^([1-9][0-9])+(.[0-9]{1,2})?$
7 带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})?$
8 正数、负数、和小数:^(-|+)?\d+(.\d+)?$
9 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11 非零的正整数:^[1-9]\d
$ 或 ^([1-9][0-9]){1,3}$ 或 ^+?[1-9][0-9]$
12 非零的负整数:^-[1-9][]0-9”$ 或 ^-[1-9]\d$
13 非负整数:^\d+$ 或 ^[1-9]\d|0$
14 非正整数:^-[1-9]\d
|0$ 或 ^((-\d+)|(0+))$
15 非负浮点数:^\d+(.\d+)?$ 或 ^[1-9]\d.\d|0.\d[1-9]\d|0?.0+|0$
16 非正浮点数:^((-\d+(.\d+)?)|(0+(.0+)?))$ 或 ^(-([1-9]\d.\d|0.\d[1-9]\d))|0?.0+|0$
17 正浮点数:^[1-9]\d.\d|0.\d[1-9]\d$ 或 ^(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9]))$
18 负浮点数:^-([1-9]\d.\d|0.\d[1-9]\d)$ 或 ^(-(([0-9]+.[0-9][1-9][0-9])|([0-9][1-9][0-9].[0-9]+)|([0-9][1-9][0-9])))$
19 浮点数:^(-?\d+)(.\d+)?$ 或 ^-?([1-9]\d.\d|0.\d[1-9]\d|0?.0+|0)$
二、校验字符的表达式
1 汉字:^[\u4e00-\u9fa5]{0,}$
2 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3 长度为3-20的所有字符:^.{3,20}$
4 由26个英文字母组成的字符串:^[A-Za-z]+$
5 由26个大写英文字母组成的字符串:^[A-Z]+$
6 由26个小写英文字母组成的字符串:^[a-z]+$
7 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
8 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9]+$
10 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11 可以输入含有^%&’,;=?$\”等字符:[^%&’,;=?$\x22]+
12 禁止输入含有~的字符:[^~\x22]+
三、特殊需求表达式
1 Email地址:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)$
2 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3 InternetURL:[a-zA-z]+://[^\s]
或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4 手机号码:^(13[0-9]|14[0-9]|15[0-9]|16[0-9]|17[0-9]|18[0-9]|19[0-9])\d{8}$ (由于工信部放号段不定时,所以建议使用泛解析 ^([1][3,4,5,6,7,8,9])\d{9}$) 5 电话号码(“XXX-XXXXXXX”、”XXXX-XXXXXXXX”、”XXX-XXXXXXX”、”XXX-XXXXXXXX”、”XXXXXXX”和”XXXXXXXX):^((\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7 18位身份证号码(数字、字母x结尾):^((\d{18})|([0-9x]{18})|([0-9X]{18}))$
8 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9
]{4,15}$
9 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
10 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.\d)(?=.[a-z])(?=.[A-Z]).{8,10}$
11 日期格式:^\d{4}-\d{1,2}-\d{1,2}
12 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
13 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
14 钱的输入格式:
15 1.有四种钱的表示形式我们可以接受:”10000.00” 和 “10,000.00”, 和没有 “分” 的 “10000” 和 “10,000”:^[1-9][0-9]
$
16 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符”0”不通过,所以我们采用下面的形式:^(0|[1-9][0-9])$
17 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]
)$
18 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
19 5.必须说明的是,小数点后面至少应该有1位数,所以”10.”是不通过的,但是 “10” 和 “10.2” 是通过的:^[0-9]+(.[0-9]{2})?$
20 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
21 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})(.[0-9]{1,2})?$
22 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})
)(.[0-9]{1,2})?$
23 备注:这就是最终结果了,别忘了”+”可以用”“替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
24 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\.[x|X][m|M][l|L]$
25 中文字符的正则表达式:[\u4e00-\u9fa5]
26 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))27 空白行的正则表达式:\n\s
\r (可以用来删除空白行)
28 HTML标记的正则表达式:<(\S?)[^>]>.?</\1>|<.? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)29 首尾空白字符的正则表达式:^\s|\s$或(^\s)|(\s$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
30 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
31 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
32 IP地址:\d+.\d+.\d+.\d+ (提取IP地址时有用)
33 IP地址:((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d)) (由@飞龙三少 提供,感谢共享)

测试工具

在线

http://tool.oschina.net/regex/
https://www.regexpal.com/
https://regex101.com/

安装包

http://deerchao.net/tools/regester/index.htm

资源