- 字面值(literals)
- 句点(dot)
- 字符类(character classes)
- 字符类区间(ranges)
- 字符类的否定(negation)
- 字符类补充
- 乘法器(multipliers)
- 乘法器区间
- 乘法器补充
- 惰性(non-greed)
- 分支(alternation)
- 组合(grouping)
- 单词边界(word boundaries)
- 行边界(line boundaries)
- 文本边界(text boundaries)
- 捕获组
- 替换
- 后向引用(back-references)
- 一些建议
- 常用正则表达式
- 正则表达式由只代表自身的字面值和代表特定含义的元字符组成
- 大部分字符,包括字母数字字符,会以字面值的形式出现,这意味着它们查找的是自身
- 除非特别说明,正则表达式是区分大小写的
- 任何单个字符
- 任何元字符使用一个饭斜杠\进行转义就会编程字面值
- 饭斜杠是一个元字符,他也可以使用饭斜杠转义
NOTICE
: 在一些实现中,.
会匹配除了换行符的任意字符
字符类是在方括号中国年的集合, 表示“找到集合里任意一个字符”
c[aeiou]t
标识找到c后跟一个元音字母,再找到t,会匹配cat
,cet
,cit
,cot
,cut
[0123456789]
表示找到一个数字[a]
和a
表示意义相同,找到a\[a\]
表示找到一个做左扩号后紧跟着一个a,再跟着一个右方括号[\[\]ab]
表示“匹配一个左方括号或者右方括号或者a或者 b”[\\\[\]]
表示“匹配一个反斜杆或者一个左方括号或者一个右方括号”
NOTICE
: 在字符类内部的“规则”和在字符类内部的规则有所不同
一些字符在字符类内部扮演着元字符的角色,但在字符类外部则充当字面值。
还有一些字符做着相反的事。一些字符在两种情形都为元字符,但在各自情形里代表不同的含义
特别地, .
表示“匹配任意字符”,但是[.]
表示“匹配句点”。不能并为一谈。
[b-f]
和[bcdef]
都表示“找到一个b或c或d或 e或f”[A-Z]
和[ABCDEFGHIJKLMNOPQRSTUVWXYZ]
都表示“匹配大写字母”[1-9
]和[123456789]
都表示“匹配一个非零数字”[0-9.,]
表示“匹配一个数字或者一个句点或者一个逗号”[0-9a-fA-F]
表示“匹配一位十六进制数”[a-zA-Z0-9\-]
表示“匹配一个字母数字字符或连字符”
NOTICE
: 区间是字符的区间,不是数字的区间。正则表达式[1-31]表示“找到一个1或一个 2或一个3”,
不是"找到一个从1到31的整数"
在最开始的位置使用插入符号(^)来否定一个字符类
[^a]
表示“匹配除了a的任意字符”[^a-zA-Z0-9]
表示“找到一个非字母也非数字的字符”[\^abc]
表示“找到一个插入符或者a或者b或者c”[^\^]
表示“找到除了插入符外的任意字符”
\d
含义与[0-9]
一致:“匹配一个数字”\w
的含义与[0-9A-Za-z_]
一致:“匹配一个单词字符\s
表示“匹配任意空白字符,空格,tab,回车或者换行\D
同[^0-9]:“匹配任意非数字的字符”\W
同[^0-9A-Za-z_]:“匹配任意非单词字符, 匹配任意不是字母,数字,下划线,汉字的字符\S
表示“匹配任意不是空白符的字符”
在一个字面值或者字符类后跟着一个大括号来使用乘法器(量词)
{1}
同a,表示“匹配一个a”a{3}
表示“找到一个a后再跟一个a,最后找到一个a”a{0}
表示“匹配空字符”a\{2\}
代表“找到一个a,跟着一个左大括号,接着跟匹配一个2,然后跟着一个右大括号”- 在字符类中大括号没有特别的含义。
[{}]
代表“匹配一个左大括号或者一个右大括号”
NOTICE
: 乘法器没有记忆。该正则表达式[abc]{2}
表示“匹配a或者b或者c,接着匹配a或者b或者c。这跟“匹配aa或ab或ac或ba或bb或bc或ca或cb或cc”相同。这跟“匹配aa或bb或cc”含义不同
x{4,4}
跟x{4}
一样colou{0,1}
r表示“匹配colour或colora{3,5}
表示“匹配aaaaa或aaaa或aaa”- 乘法器是贪婪的,优先匹配较长的
a{1,}
表示“在一列中找到一个或多个a”.{0,}
表示“匹配任何情形”
PRACTICE
:
- 编写一个能匹配双引号字符串的正则表达式,同时该字符串可以拥有任意数量的字符,
".{0,}"
- 修改上面的正则表达式,来找到了双引号字符串,但它们之间没有多余的双引号,
"[^"]{0,}"
?
代表的含义与{0,1}
相同*
等于{0,}
+
等于{1,}
\?\*\+
表示“匹配一个问号,接着找到一个星号,然后跟着一个加号”[?*+]
表示“找到一个问号或者一个星号或者一个加号”
正则表达式".*"
表示“找到一个双引号,接着找到尽可能多的字符,最后再找到一个双引号”。注意一下被.*
匹配的内部字符,很可能包含多个双引号。这通常不是非常有用
\d{4,5}?
表示“匹配\d\d\d\d
或\d\d\d\d\d
”。其实跟\d{4}
行为一致colou??r
就是colou{0,1}?r
,表示“找到color
或colour
”。和colou?r
行为一致.*?"
表示“匹配一个双引号,跟着一个尽可能少的字符,再跟着一个双引号”
使用管道符号来实现匹配多种选择
cat|dog
表示“匹配cat或dog”red|blue|
和red||blue以及|red|blue都是同样的意思,“匹配red或blue或空字符串”a|b|c
跟[abc]
一样cat|dog|\|
表示“匹配cat或dog或管道符号”[cat|dog]
表示“找到a或c或d或d或g或o或t或一个管道符号”
PRACTICE
: 编写一个正则表达式匹配1到31(含)之间的整数 [1-9]|[12][0-9]|3[01]
- 在一周中找到一天,使用
(Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day
(\w*)ility
等同于\w*ility
\(\)
表示“匹配一个左圆括号后,再匹配一个右圆括号”[()]
表示“匹配一个左圆括号或一个右圆括号”(red|blue|)
表示“匹配red或blue或空字符串”abc()def
等同于abcdef
(red|blue)?
等同于(red|blue|)
\w+(\s+\w+)*
代表“找到一个或多个单词,它们以空格隔开
单词边界是一个单词字符和非单词字符之间的位置。记住,一个单词字符是\w
,它是[0-9A-Za-z_]
,一个非单词字符是\W
,也就是[^0-9A-Za-z_]
文本的开头和结尾总是当作单词边界
- 正则表达式
\b
表示“匹配一个单词边界” \b\w\w\w\b
表示“匹配一个三个字母的单词”a\ba表
示“找到a
,跟着一个单词边界,接着找到b
”
文本不是以换行符结束,而是以行结束。然而,任何行,包括最后一行,可以包含零个字符。 起始行位置是在一个换行符和下一行的第一个字符之间。与单词边界一样,在文本的开头也算作一个起始的行。 结束行位置是在行的最后一个字符和换行符之间。与单词边界一样,文本结束也算作行结束
- 起始行,行,结束行
- 换行
- 开始行,行,结束行
- 换行
- ...
- 换行
- 开始行,行,结束行
在此基础上:
- 正则表达式
^
表示“匹配开始行” - 正则表达式
$
表示“匹配结束行” ^$
表示“匹配空行”^.*$
将会匹配整个文本,因为换行符是一个字符,所以.
会匹配它。为了匹配单行,要使用惰性乘法器,^.*?$
\^\$
表示“匹配尖符号后跟着一个美元符号”[$]
表示“匹配一个美元符”[^]
是非法单正则表达式- 尖符号在方括号中时有不同的特殊含义。把尖符号放在字符类中,这么用
[\^]
很多实现提供一个标记,通过改变它来改变^
和$
的含义。从“行开始”和“行结束”变成“文本开始”和“文本结束”
其它的一些实现提供单独的元字符\A
和\z
来达到这个目的
捕获组从左到右进行编号,只要计算左圆括号
如果正则表达式是(\w+) had a ((\w+) \w+)
。如果我们的输入文本是I had a nice day
,那么
- 捕获组1是
I
- 捕获组2是
nice day
- 捕获组3是
nice
NOTICE
: 从一个成功返回的匹配中捕获组数量总是等于原来正则表达式中捕获组的数量
正则表达式((cat)|dog)
表示“匹配cat或dog”。这里总是存在两组捕获组。如果我们的输入文本是dog,那么捕获组1是dog,捕获组2是空字符串,因为另一个选择未被使用
正则表达式a(\w)*
表示“匹配一个以a开头的单词”。这里总是只有一个捕获组
- 如果输入文本是a,捕获组1是空字符串
- 如果输入文本是ad,捕获组1是d
- 如果输入文本是avocado,捕获组1是v。然而,捕获组0会是整个单词,avocado
一旦你用了正则表达式来查找字符串,你可以指定另一个字符串来替换它,第二个字符串时替换表达式
尝试去用ISO 8691格式的日期(YYYY-MM-DD)去替换美式日期(MM/DD/YY)
- 通过正则表达式
(\d\d)/(\d\d)/(\d\d)
开始。注意这里有三个捕获组:月,日和两个数字表示的年 - 通过使用一个反斜杆和一个捕获组号来引用一个捕获组。所以,你的替换表达式为
20\3-\1-\2
- 如果我们的输入文本是
03/04/05
(表示 3月4号,2005年),那么- 捕获组1是
03
- 捕获组2是
04
- 捕获组3是
05
- 替换字符串为
2005-03-04
- 捕获组1是
- 使用正则表达式
([aeiou])
和替换表达式\1\1
来让元音翻倍 - 正则表达式
([\\"])
中,捕获组1是双引号或者反斜杆 - 替换表达式
\\\1
中,一个字面值反斜杆后跟着一个匹配的双引号或者反斜杆
在同样的表达式中引用同一个捕获组,这称为后向引用
表达式[abc]{2}
表示“匹配aa或ab或ac or ba或bb或bc或ca或cb或cc”。但是表达式([abc])\1
表示“匹配aa或bb或cc”
正则表达式能用于用户输入验证。但过于严格的验证会让用户感到难受。
我在网页上输入我的卡号如1234 5678 8765 4321。会被这个站点拒绝。因为它使用\d{16}来进行验证。 该正则表达式允许出现空格和连字符。 其实,为什么不直接去掉所有非数字字符,然后再进行验证?要做到这一点,使用正则表达式\D和空字符串来替换表达式
写一个正则表达式,可以验证我的卡号而不用让我删去非数字字符 \D*(\d\D*){16}
不要使用正则表达式来验证用户的名字。其实,不需要验证名字,你无能无力
- 名字不能包含空格。
- 名字不能包含标点符号。
- 名字只能使用ASCII字符。
- 名字会被限制在任何特定的字符集。
- 名字总是有像M字符那么长。
- 人总是有且只有一个用的名字。
- 人总是有且仅有一个中间名。
- 人总是有且只有一个姓。
- ...
不要使用正则表达式来验证邮件地址
首先,这很难保证正确无误。电子邮件地址确实符合一个正则表达式,但是这个表达式长又复杂地让人联想到世界末日。任何缩略都会可能产生遗漏(false negatives)
其次,即使所提供的电子邮件地址符合正则表达式,但也并不能证明它的存在。验证电子邮件地址的唯一方法是发送电子邮件给它
在正式的应用中,不要使用正则表达式来解析HTML或XML
解析HTML/XML是
- 不可能使用简单的正则 一般来说很难 一个已解决了的问题
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[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
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 身份证号(15位、18位数字):^\d{15}|\d{18}$
8 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
9 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
10 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
11 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
12 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
14 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15 钱的输入格式:
16 1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
17 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
18 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
19 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
20 5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
21 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
22 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
23 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
24 备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
25 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
26 中文字符的正则表达式:[\u4e00-\u9fa5]
27 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
28 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
29 HTML标记的正则表达式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
30 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
31 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
32 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
33 IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
34 IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))