基本定义和语法
基本定义
正则表达式是一个描述字符模式的对象,JavaScript 的 RegExp
类表示正则表达式。JavaScript 的正则表达式语法时 Perl5 的正则表达式语法的大型子集。
JavaScript 中的正则表达式用 RegExp
对象表示,可以使用 RegExp()
构造函数来创建 RegExp 对象,但更多是通过直接量语法来创建,在 JavaScript 中,正则表达式的直接量定义为包含在一对/
之间的字符:
表明运行这段代码创建的是一个 RegExp
对象,用于表示所有以字母 s
结尾的字符串。以上表达式和下面的表达式等价:
var pattern = new RegExp("s$");
需要注意的是,使用对象/数组直接量创建对象/数组的时候,同一段代码每次都会创建新对象/数组,但是 ECMAScript 3 中,一个正则表达式直接量会在执行到它时转换为一个 RegExp 对象,同一段代码每次运算都返回同一个对象,而在 ECMAScript 5 中,则和对象/数组一样,每次运算都返回新对象。
直接量字符
JavaScript 正则表达式中的所有字母和数字都是按照字面含义进行匹配的,比如 /javascript/
可以匹配所有包含 javascript
的字符串,此外还支持非字母的字符匹配,这些字符需要通过反斜杠\
进行转义,构成转义字符,比如转义字符\n
用于匹配换行符:
字符 | 匹配 |
---|---|
\o | NUL字符 |
\t | 制表符 |
\n | 换行符 |
\v | 垂直制表符 |
\f | 换页符 |
\r | 回车符 |
\xnn | 十六进制数nn指定的字符 |
\uxxxx | 十六进制数xxxx指定的Unicode字符 |
\cX | 控制字符^X |
\ | 反斜杠\ |
在正则表达式中还有很多标点符号表示特殊的含义:
^ $ . * + ? = ! : | \ / ( ) [ ] { }
在接下来的季节里我们将学习这些符号的含义。
字符类
将直接量字符单独放到方括号内就组成了字符类,一个字符类可以包含它所包含的任意字符,比如 /[abc]/
可以和字符 a、b、c 中的任意一个匹配。另外,可以通过 ^
来定义否定字符类,它匹配所有不包含在方括号中的字符。此外,还可以在字符类中使用连字符表示字符范围,比如 /a-z/
可以用来匹配小写英文字母,要匹配所有字母和数字,可以使用 /a-zA-Z0-9/
。
由于某些字符类非常常用,所以 JavaScript 定义了一些特殊的转义字符来表示它们:
字符 | 匹配 |
---|---|
[...] | 方括号内的任意字符 |
[^...] | 不在方括号内的任意字符 |
. | 除换行符及其他Unicode行终止符之外的任意字符 |
\w | 任何ASCII字符组成的单词,等价于[a-zA-Z0-9] |
\W | 任何非ASCII字符组成的单词,等价于[^a-zA-Z0-9] |
\s | 任何Unicode空白符 |
\S | 任何非Unicode空白符的字符 |
\d | 任何ASCII数字,等价于[0-9] |
\D | 除ASCII数字之外的任意字符,等价于[^0-9] |
[\b] | 退格符(特例) |
重复
用上面表格中列出的正则表达式语法,可以将两个数字表示成 /\d\d/
,但是如果有100个数字呢,我们可以在正则模式之后紧随用以描述字符重复的标记来描述任意多位的字符或数字,为此,有一些专门用于表示这种情况的特殊字符:
字符 | 匹配 |
---|---|
{n,m} | 匹配前一项至少n次,但不能超过m次 |
{n,} | 匹配前一项至少n次 |
{n} | 匹配前一项n次 |
? | 匹配前一项0次或1次,等价于{0,1} |
+ | 匹配前一项1次或多次,等价于{1,} |
* | 匹配前一项0次或多次,等价于{0,} |
例如:
/\d{3,4}/ // 匹配3~4个数字
/\w{3}\d?/ // 精确匹配3个单词和一个可选的数字
/\s+php\s+/ // 匹配前后带有一个或多个空格的字符串「php」
非贪婪的重复
上面表格列出的匹配重复字符是尽可能多的匹配,而且允许后续的正则表达式继续匹配,因此称之为「贪婪的」匹配。我们同样可以使用正则表达式进行非贪婪的匹配,只需在待匹配的字符后面跟上一个问号即可:??
、+?
、*?
或者 {1,5}?
,举个例子:
我们使用贪婪匹配会匹配出「aaa」,而使用非贪匹配只能匹配一个「a」。
但是有时候非贪婪匹配的结果可能会和预期不一致,我们来看这个例子:
非贪婪匹配和贪婪匹配的结果是一样的,这是因为正则表达式的模式匹配总是会寻找字符串中第一个可能匹配的位置,由于该匹配是从字符串的第一个字符开始的,因此,在这里不考虑它的子串中更短的匹配。
选择、分组和引用
正则表达式的语法还包括指定选择项、子表达式分组和引用前一子表达式的特殊字符。
字符「|」用于分隔供选择的字符,例如,/ab|cd|ef/
可以匹配 ab
,也可以匹配 cd
,还可以匹配 ef
。需要注意的是,选择项的尝试匹配次序是从左到右的,直到发现匹配项,如果左边的选择项匹配,就忽略右边的匹配项,即使能够产生更好的匹配,比如:
就只匹配出了「a」。
正则表达式中的圆括号有多种作用。一个作用是把单独的项组后成子表达式,以便可以像处理独立单元那样对其进行处理,例如 /java(script)?/
可以匹配出 java
,其后可以有 script
也可以没有;另一个作用是在完整的模式中定义子模式,当一个正则表达式成功和目标字符串匹配时,可以从目标串中抽出和圆括号中的子模式相匹配的部分,例如:
我们就可以从中抽出版本号单独进行处理。
此外,带圆括号的表达式还有一个用途是允许在同一正则表达式的后面引用前面的子表达式,这是通过在字符「\」后面加一位或多位数字来实现的,这个数字指定了带圆括号的子表达式在正则表达式中的位置。对正则表达式中前一个子表达式的引用,并不是对子表达式模式的引用,而是与那个模式相匹配的文本的引用。我们将在后面演示子表达式的引用,这是正则表达式检索和替换操作的强大特性之一。
在正则表达式中不用创建带数字编码的引用,也可以对子表达式进行分组,它不是以「(」和「)」进行分组,而是以「(?:」和「)」进行分组:
/([Jj]ava(?:[Ss]cript)?)\sis\s(fun\w*)/
这里,子表达式(?:[Ss]cript?)
仅仅用于分组,这种改进的圆括号并不生成引用,所以在这个正则表达式中,\2
引用了与(fun\w*)
匹配的文本:
下表列出了正则表达式的选择、分组、引用字符:
字符 | 含义 |
---|---|
| |
选择,匹配该字符左边或右边的表达式 |
(...) | 组合,将几个项组成一个单元,这个单元通过* 、+ 、? 、| 等符号进行修饰,而且可以记住和这个组合相匹配的字符串以供后续引用 |
(?:...) | 只组合,不可引用 |
\n | 和第n个分组第一次匹配的字符相匹配,分组是圆括号中的子表达式,n是从左到右左括号索引数 |
指定匹配位置
有些正则表达式元素不匹配某个可见的字符,而是指定匹配发生的合法位置,有时我们称这些元素为正则表达式的锚,因为它们将模式定位在搜索字符串的特定位置上。最常见的锚元素是 ^
和 $
分别用于匹配字符串的开始和结束。常用的正则表达式的锚字符定义如下:
字符 | 含义 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的结尾 |
\b | 匹配一个单词的边界,简言之,就是位于字符\w 和\W 之间的位置,或位于\w 与字符串开头或结尾的位置 |
\B | 匹配非单词边界的位置 |
(?=p) | 零宽正向先行断言,要求接下来的字符都与p 匹配,但不能包括匹配p 的那些字符 |
(?!p) | 零宽负向先行断言,要求接下来的字符不与p 匹配 |
举写例子来演示下上述模式更直观:
修饰符
关于正则表达式还有最后一个知识点,那就是正则表达式的修饰符,用以说明高级匹配模式的规则。和之前讨论的正则表达式语法不同,修饰符是放在「/」符号之外的,确切说是第二条斜线之后。JavaScript 中支持三个修饰符,并且这些修饰符可以组合使用:
字符 | 含义 |
---|---|
i |
用于说明匹配模式不区分大小写 |
g |
用于说明模式匹配应该是全局的,即检索出所有匹配 |
m |
用于在多行模式中执行匹配 |
No Comments