声明:本博客的知识点摘录自《JavaScript高级程序设计(第三版)》第五章,如有冒犯,请联系删除
本章只介绍JavaScript的RegExp类型,不介绍相关语法,如要学习语法,请点击这里
1、RegExp 是什么东东?
RegExp是一种类型,JavaScript用这个类型来支持正则表达式,创建正则表达式的语法如下:
var expression = / pattern / flags ;
其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags) ,用以标明正则表达式的行为。正则表达式的匹配模式支持下列 3 个标志:
- g :表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
- i :表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
- m :表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
因此,一个正则表达式就是一个模式与上述 3 个标志的组合体。不同组合产生不同结果,如下面的例子所示:
/*
* 匹配字符串中所有"at"的实例
*/
var pattern1 = /at/g;
/*
* 匹配第一个"bat"或"cat",不区分大小写
*/
var pattern2 = /[bc]at/i;
/*
* 匹配所有以"at"结尾的 3 个字符的组合,不区分大小写
*/
var pattern3 = /.at/gi;
与其他语言中的正则表达式类似, 模式中使用的所有元字符都必须转义。 正则表达式中的元字符包括:
( [ { \ ^ $ | ) ? * + .]}
这些元字符在正则表达式中都有一或多种特殊用途,因此如果想要匹配字符串中包含的这些字符,就必须对它们进行转义。下面给出几个例子:
/*
* 匹配第一个"bat"或"cat",不区分大小写
*/
var pattern1 = /[bc]at/i;
/*
* 匹配第一个" [bc]at",不区分大小写
*/
var pattern2 = /\[bc\]at/i;
/*
* 匹配所有以"at"结尾的 3 个字符的组合,不区分大小写
*/
var pattern3 = /.at/gi;
/*
* 匹配所有".at",不区分大小写
*/
var pattern4 = /\.at/gi;
2、怎么创建正则表达式呢?
前面已经介绍了一种创建方式,即采用字面量的方式,下面介绍通过RegExp类型的构造函数来创建:
/*
* 匹配第一个"bat"或"cat",不区分大小写
*/
var pattern1 = /[bc]at/i;
/*
* 与 pattern1 相同,只不过是使用构造函数创建的,构造函数接收两个参数:一个是要匹配的字符串模式,另一个是可选的标志字符串。
*/
var pattern2 = new RegExp("[bc]at", "i");
由于 RegExp 构造函数的参数是字符串,所以在某些情况下要对字符进行双重转义,所有元字符都必须双重转义:
3、两种创建方式的区别
正则表达式字面量始终会共享同一个 RegExp 实例,而使用构造函数创建的每一个新 RegExp 实例都是一个新实例。来看下面的例子。
var re = null,
i;
for (i=0; i < 10; i++){
re = /cat/g;
re.test("catastrophe");
}
for (i=0; i < 10; i++){
re = new RegExp("cat", "g");
re.test("catastrophe");
}
在第一个循环中,即使是循环体中指定的,但实际上只为 /cat/ 创建了一个 RegExp 实例。由于实例属性(下一节介绍实例属性)不会重置,所以在循环中再次调用 test() 方法会失败。这是因为第一次调用 test() 找到了 "cat" ,但第二次调用是从索引为 3 的字符(上一次匹配的末尾)开始的,所以就找不到它了。由于会测试到字符串末尾,所以下一次再调用 test() 就又从开头开始了。第二个循环使用 RegExp 构造函数在每次循环中创建正则表达式。因为每次迭代都会创建一个新的RegExp 实例,所以每次调用 test() 都会返回 true 。ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用 RegExp 构造函数一样,每次都创建新的 RegExp 实例。IE9+、Firefox 4+和 Chrome 都据此做出了修改。
4、RegExp属性
- global :布尔值,表示是否设置了 g 标志。
- ignoreCase :布尔值,表示是否设置了 i 标志。
- lastIndex :整数,表示开始搜索下一个匹配项的字符位置,从 0 算起。
- multiline :布尔值,表示是否设置了 m 标志。
- source :正则表达式的字符串表示,按照字面量形式返回。
5、RegExp方法
5.1 exec()方法
exec() 接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回 null 。
返回的数组虽然是 Array 的实例,但包含两个额外的属性: index 和 input 。其中, index 表示匹配项在字符串中的位置,而 input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项) 。
var text = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(text);
alert(matches.index); // 0
alert(matches.input); // "mom and dad and baby"
alert(matches[0]); // "mom and dad and baby"
alert(matches[1]); // " and dad and baby"
alert(matches[2]); // " and baby"
对于 exec() 方法而言,即使在模式中设置了全局标志( g ) ,它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 exec() 则都会在字符串中继续查找新匹配项,如下面的例子所示。
var text = "cat, bat, sat, fat";
var pattern1 = /.at/;
var matches = pattern1.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern1.lastIndex); //0
matches = pattern1.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern1.lastIndex); //0
var pattern2 = /.at/g;
var matches = pattern2.exec(text);
alert(matches.index); //0
alert(matches[0]); //cat
alert(pattern2.lastIndex); //3
matches = pattern2.exec(text);
alert(matches.index); //5
alert(matches[0]); //bat
alert(pattern2.lastIndex); //8
5.2 test()方法
test()方法接受一个字符串参数。在模式与该参数匹配的情况下返回true ;否则,返回 false 。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便。
var text = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
alert("The pattern was matched.");
}
5.3 toLocaleString() 和 toString() 方法
RegExp 实例继承的 toLocaleString() 和 toString() 方法都会返回正则表达式的字面量,与创建正则表达式的方式无关。
var pattern = new RegExp("\\[bc\\]at", "gi");
alert(pattern.toString()); // /\[bc\]at/gi
alert(pattern.toLocaleString()); // /\[bc\]at/gi
即使上例中的模式是通过调用 RegExp 构造函数创建的,但 toLocaleString() 和 toString()方法仍然会像它是以字面量形式创建的一样显示其字符串表示。
tips:正则表达式的 valueOf() 方法返回正则表达式本身。
6、RegExp 构造函数属性
使用这些属性可以从 exec() 或 test() 执行的操作中提取出更具体的信息。请看下面的例子。
var text = "this has been a short summer";
var pattern = /(.)hort/g;
/*
* 注意:Opera 不支持 input、lastMatch、lastParen 和 multiline 属性
* Internet Explorer 不支持 multiline 属性
*/
if (pattern.test(text)){
alert(RegExp.input); // this has been a short summer
alert(RegExp.leftContext); // this has been a
alert(RegExp.rightContext); // summer
alert(RegExp.lastMatch); // short
alert(RegExp.lastParen); // s
alert(RegExp.multiline); // false
}
除了上面介绍的几个属性之外,还有多达 9 个用于存储捕获组的构造函数属性。访问这些属性的语法是 RegExp.$1 、 RegExp.$2 … RegExp.$9 ,分别用于存储第一、第二……第九个匹配的捕获组。在调用 exec() 或 test() 方法时,这些属性会被自动填充。
var text = "this has been a short summer";
var pattern = /(..)or(.)/g;
if (pattern.test(text)){
alert(RegExp.$1); //sh
alert(RegExp.$2); //t
}
这里创建了一个包含两个捕获组的模式,并用该模式测试了一个字符串。即使 test() 方法只返回一个布尔值,但 RegExp 构造函数的属性 $1 和 $2 也会被匹配相应捕获组的字符串自动填充。
7、模式的局限性
下面列出ECMAScript 不支持的一些高级特性:
- 匹配字符串开始和结尾的 \A 和 \Z (但支持以插入符号(^)和美元符号($)来匹配字符串的开始和结尾)
- 向后查找(lookbehind)( 但完全支持向前查找(lookahead))
- 并集和交集类
- 原子组(atomic grouping)
- Unicode 支持(单个字符除外,如 \uFFFF )
- 命名的捕获组( 但支持编号的捕获组。)
- s (single,单行)和 x (free-spacing,无间隔)匹配模式
- 条件匹配
- 正则表达式注释
即使存在这些限制,ECMAScript 正则表达式仍然是非常强大的,能够帮我们完成绝大多数模式匹配任务。