第 1 章 字符组
字符组就是“一组”字符。在正则表达式中,它表示“在同一个位置可能出现的各种字符”。
写法:在一对方括号[
和]
之间,列出所有可能出现的字符。例如:[abc]、[123]、[#?.]等
1.1 普通字符组
例如:检测字符串中是否存在数字
/[0123456789]/.test('hello123world');
字符组中字符的排列顺序并不影响字符组的功能,出现重复字符也不会影响。例如:[0123456789]
完全等价于[9876543210]
、[998876543210]
。
为了代码更容易编写、方便阅读,不推荐在字符组中出现重复字符,而且还应该让字符组中的字符排列更符合认知习惯。为此,正则表达式提供了**-范围表示法(range)**,它更直观,能进一步简化字符组。
所谓“-范围表示法”,就是用[x-y]
的形式,表示 x 到 y 整个范围内的字符。[0123456789]
就可以表示为[0-9]
。
1.1.1 范围表示法
“-范围表示法”的范围是如何确定的?为什么要写作
[0-9]
,而不写作[9-0]
?
在字符组中,-
表示的范围,一般是根据字符对应的码值(字符在对应编码表中的编码数值)来确定的。码值小的字符在前,码值大的字符在后。在ASCII编码中,字符 0 的码值是48(十进制),字符9的码值是57(十进制)。所以[0-9]
等价于[0123456789]
。
/[0-9]/.test('2')
在字符组中可以同时并列多个“-范围表示法”。例如:[0-9a-fA-F]
可以匹配数字,大小写形式的a ~ f,它可以用来验证十六进制字符。
1.1.2 转义序列xnum
表示字符
可以使用转义序列xnum
来表示一个字符。其中x
是固定前缀,表示转义序列的开头,num
是字符对应的码值,是一个两位的十六进制数值。例如:字符A的码值是41(十进制则为65),所以也可以用x41
表示。
字符组中有时会出现这种表示法,它可以表示一些难以输入或者难以显示的字符,比如x7f
。也可以用来方便地表示某个范围,例如:
- 所有标准ASCII字符对应的字符组:
[x00-x7f]
,匹配码值范围[0,127]
。 - 所有扩展ASCII字符对应的字符组:
[x00-xff]
,匹配码值范围[0,255]
,可以用来匹配非中文字符。
1.2 元字符与转义
字符组中的横线
-
并不能匹配横线字符,而是用来表示范围,这类字符叫元字符。
字符组的开方括号[
、闭方括号]
、横线-
、尖角号^
都算元字符,在匹配中,有着特殊的意义。但有时候并不需要表示特殊的意义,只需要表示普通字符,此时就必须做特殊处理。
字符组中的-
,如果它紧邻着字符组中的开方括号[
或闭方括号]
,那么它就是普通字符,其他情况下都是元字符。
/[-9]/.test('-'); // true
/[9-]/.test('-'); // true
而对于其他元字符,取消特殊含义的做法都是转义,也就是在正则表达式中的元字符前加上反斜线字符。
使用RegExp
构造器创建正则,转义字符组中的横线-
:
new RegExp('[0\-2]');
仔细观察会发现,在正文里说“在正则表达式中的元字符之前加上反斜线字符”,而在代码里写的却不是
[0-2]
,而是[0\-2]
。因为在这段程序里,正则表达式是以字符串的方式提供的,而字符串本身也有关于转义的规定。上面说的“正则表达式”,是经过“字符串转义处理”之后的字符串的值。因为处理字符串时,反斜线和它之后的字符会被认为是转义序列。因此需要\
转义成。
1.3 排除型字符组
在方括号[...]
中列出希望匹配的所有字符,这种字符组可以叫做“普通字符组”。它的确非常方便,但也有些问题是普通字符组不能解决的。比如匹配字符串中是否存在非数字字符,不是数字的字符太多了,全部列出几乎不可能,这时就应当使用排除型字符组。
排除型字符组非常类似普通字符串[...]
,只是在开方括号[
之后紧跟一个尖角号^
,写作[^...]
,表示“在当前位置,匹配一个没有列出的字符”。所以[^0-9]
就表示“0 ~ 9之外的字符”,也就是“非数字字符”。
注意:排除型字符组必须匹配一个字符。
// 匹配一个除-、0、9之外的字符
/[^-09]/.test('-'); // false
// 匹配一个除0~9之外的字符
/[^0-9]/.test('a'); // true
在排除型字符组中,^
是一个元字符,但只有它紧跟在[
之后时才是元字符。如果想表示“这个字符组中可以出现^字符”,不要让它紧挨着[
即可,否则就要转义。
/[0^9]/.test('^'); // true
1.4 字符组简记法
用[0-9]
、[a-z]
等字符组,可以很方便地表示数字字符和小写字母字符。对于这类常用的字符组,正则表达式提供了更简单的记法,这就是字符组简记法。
常见的字符组简记法有d
、w
、s
。
d
等价于[0-9]
,其中的 d 代表“数字(digit)”。w
等价于[0-9a-zA-Z_]
,其中的 w 代表“单词字符(word)”。s
等价于[ nrtvf]
(第一个字符是空格),其中的 s 代表“空白字符(space)”。空白字符,可以是空格字符、新行换行符n
、回车换行符r
、水平制表符t
、垂直制表符v
、换页符f
。
/d/.test('2'); // true
/w/.test('a'); // true
/w/.test('_'); // true
/w/.test('2'); // true
/s/.test(' '); // true
字符组简记法可以单独出现,也可以使用在字符组中。比如[0-9a-zA-Z]
可以写成[da-zA-Z]
。
字符组简记法也可以用在排除型字符组中,比如[^0-9]
可以写成[^d]
。
相对于d
、w
、s
这三个普通字符组简记法,正则表达式也提供了对应排除型字符组的简记法:D
、W
、S
。字母完全一样,只是改为大写。
D
等价于[^d]
、[^0-9]
W
等价于[^w]
、[^0-9a-zA-Z_]
S
等价于[^s]
、[^ nrtvf]
// d 和 D
/d/.test('8'); // true
/d/.test('a'); // false
/D/.test('8'); // false
/D/.test('a'); // true
// w 和 W
/w/.test('c'); // true
/w/.test('!'); // false
/W/.test('c'); // false
/W/.test('!'); // true
// s 和 S
/s/.test('t'); // true
/s/.test('0'); // false
/S/.test('t'); // false
/S/.test('0'); // true
注意:[sS]
、[wW]
、[sS]
能匹配任意字符。
关于字符组简记法,最后需要补充三点:
-
,否则可能会引起错误,比如[d-a]
就很让人困惑。d
、w
、s
的匹配规则,都是针对ASCII编码而言的,也叫ASCII匹配规则。而在一些语言中的正则表达式已经支持了Unicode字符。那么数字字符、单词字符、空白字符的范围,已经不仅限于ASCII编码中的字符。第 2 章 量词
2.1 一般形式
验证中国大陆地区的邮政编码(6位数字构成的字符串),比如
201203
。
只有同时满足以下两个条件,匹配才成功:
/^dddddd$/.test('201203'); // true
^
表示字符串的开头位置,$
表示字符的结尾位置。
d
重复了6次,读写都不方便。为此,正则表达式提供了量词。比如上面匹配邮政编码的表达式,就可以简写为d{6}
,它使用阿拉伯数字,更简洁直观。
/^d{6}$/.test('201203'); // true
量词 | 说明 |
---|---|
{n} |
之前的元素必须出现 n 次 |
{n,m} |
之前的元素最少出现 n 次,最多出现 m 次 |
{n,} |
之前的元素最少出现 n 次,最多无上限(隐式上限 65536) |
{0,n} |
之前的元素可以不出现,也可以出现,最多出现 n 次 |
注意:量词中的逗号之后不能有空格。
术语:
- 结构:一般指的是正则表达式所提供功能的记法。比如字符组就是一种结构。
- 元素:指的是具体的正则表达式中的某个部分,比如某个具体表达式中的字符组
[a-z]
,可以算作一个元素。元素,也叫“子表达式”。
2.2 常用量词
{n,m}
是通用形式的量词,正则表达式还有3个常用量词,分别是+
、*
、?
。它们的形态虽然不同于{n,m}
,功能却是相同的(也可以把它们理解为“量词简记法”)。
常用量词 | {n,m}等价形式 | 说明 |
---|---|---|
* |
{0,} |
可能不出现,也可能出现,出现次数没有上限 |
+ |
{1,} |
至少出现 1 次,出现次数没有上限 |
? |
{0,1} |
至多出现 1 次,也可能不出现 |
/^https?$/.test('http'); // true
/^https?$/.test('https'); // true
HTML标签匹配,分为开始标签、结束标签、自闭合单标签。例如:就是开始标签,
就是结束标签,
就是自闭合单标签。
/^
- 以
结束。
前不能是/、空白字符。
/^$/
- 以
结束。
- 以
或
/>
结束
- 以