2 min read

JavaScript 正则规则的匹配

上一篇 JavaScript 取得所有匹配字符串中颇多遗漏,所以补一篇。

在 JavaScript 中,取得匹配字符串主要有两种方法,一种是利用 String 对象的 match() 方法,一种是 RegExp 对象的 exec() 方法。

预设一个文本变量 str:

var str = "this is a long story. And we are so sorry.";

要取得 「story」 与 「sorry」。

String 对象的 match 方法

代码如下,match 方法会返回一个数组,数组的每个元素都是匹配项,有 g 的话,会进行全局匹配,alert 命令显示 「story」 和 「sorry」;没有 g 的话,就只显示 「story」,即数组中只有一个元素。

//********代码1
var str = "this is a long story. And we are so sorry.";
var re = /s[^\s]+y/g;
var ss = str.match(re);
var i = 0;
while (i < ss.length){
    alert(ss[i]);
    i++;
}

但是,在没有全局匹配 g 这个 flag 的情况下,还有一种特殊情况,就是正则表达式中包括了用一对括号 () 括起来的子规则 – 更常见的说法可能是「分组」,

//********代码2
var str = "this is a long story. And we are so sorry.";
var re = /s([^\s]+)y/;//这个正则表达式中有一对括号
var ss = str.match(re);
var i = 0;
while (i < ss.length){
    alert(ss[i]);
    i++;
}

这里,alert 命令显示的结果是 「story」 与 「tor」,即 match 返回的数组中包含两个元素,一个是匹配的第一个字符串,一个是正则规则中括号中的子规则匹配的部分字符串。

RegExp 对象的 exec 方法

exec() 方法跟 String 对象的 match() 方法颇为接近,先来说正则规则中不带括号的情况:

//********代码3
var str = "this is a long story. And we are so sorry.";
var re = /s[^\s]+y/;
var ss = re.exec(str);//与 match() 方法对比,主要是改了这一行
var i = 0;
while (i < ss.length){
    alert(ss[i]);
    i++;
}

上述代码只显示 「story」。这很容易理解。

但是,正则规则里即使使用全局匹配的标志 g,结果也是一样,浏览器里只弹出 「story」。这是与 String 对象的 match() 方法不同的地方。

不过 g flag 也不是什么都没做,它会将正则对象 re 的 lastIndex 属性值设置为匹配字符串后一位位置值。比如上述代码匹配了 「story」,这个单词后一个字符为「.」,其在文本 str 中的位置为20,这样当 re 正则对象第二次被调用时,它将从 lastIndex 所指示的位置开始查找,如果没有找到匹配的字符串,就将 lastIndex 值设置为0 – 因为代码3中直接书写 var ss = re.exec(str);,这就导致正则对象只调用了一次 exec() 方法,也就取不到所有的匹配项。除非使用上一篇提到的办法:

//********代码4
var str = "this is a long story. And we are so sorry.";
var re = /s[^\s]+y/g;
var ss;
while ((ss = re.exec(str)) !== null){
    alert(ss[0]);
}

这样,我们就捕捉到所有匹配的字符串 – 包括 「story」 和 「sorry」。

对比两类方法,如果是匹配全部的字符串的话,String 对象的 match 方法显然要比 RegExp 对象的 exec 方法简便易懂多了。

两种特殊情况

不管是 String 对象的 match 方法还是 RegExp 对象的 exec 方法,在没有 g 标志的情况下,正则表达式中如果使用括号 – 即含有子规则,它们的处理方法是一样的:都是一个数组,数组中第一个元素是匹配到的第一个字符串,然后是子规则匹配到的字符串。

但如果全局匹配的情况下兼又含有子规则,则情况又有些不同。

先说 RegExp 对象的 exec 方法:

//********代码5
var str = "this is a long story. And we are so sorry.";
var re = /s([^\s]+)y/g;
var ss;
while ((ss = re.exec(str)) !== null){
    alert(ss[0]);
    alert(ss[1]);
}

上述代码会在浏览器窗口中弹出四个对话框,对话框内容分别是 「story」、「tor」、「sorry」、「orr」。即是说,每次执行 exec 方法时,都会返回一个带有两个元素的数组,一个为匹配的字符串,一个为子规则匹配的字符串。

String 对象的 match 方法颇有点不同:

//********代码6
var str = "this is a long story. And we are so sorry.";
var re = /s([^\s]+)y/g;//这个正则表达式中有一对括号
var ss = str.match(re);
alert(ss.join("-"));

因为我不知道 ss 这个返回的数组里到底是什么东西,就用数组的 join() 方法将其联合起来,结果是 「story-sorry」,没有任何 「tor」、「orr」 的东西,这里,括号所指示的子规则作用被忽视。

报告问题 修订