JavaScript 网页编程从入门到精通 (清华社"视频大讲堂"大系·网络开发视频大讲堂)
上QQ阅读APP看书,第一时间看更新

8.6 使用RegExp对象

在JavaScript中,每个正则表达式都是一个对象。正则表达式是使用RegExp对象来表示的,除了RegExp()构造函数外,RegExp对象还拥有很多属性和方法,熟练掌握这些方法和属性的使用,能够提高正则表达式的应用技巧。

8.6.1 实例属性

RegExp类比较特殊,它不仅定义了类(即静态)属性,还定义了实例属性。也就是说,它既定义了属于构造函数RegExp()的全局属性,又定义了属于RegExp对象的具体实例属性。本节将讲解正则表达式的实例属性,说明如表8-8所示。

表8-8 RegExp对象的实例属性

【示例1】对于任何一个正则表达式对象来说,它都会拥有这5个属性。不过由于可以使用标志来设置正则表达式的这些属性,所以一般很少使用它们。

      var r=/a/gi;                                     //声明正则表达式直接量
      alert(r.global);                                 //返回true
      alert(r.ignoreCase);                             //返回true
      alert(r.multiline);                              //返回false
      alert(r.source);                                 //返回a

注意:global、ignoreCase、multiline和source属性都是只读属性。

【示例2】lastindex属性比较有用,它是可以读写的。对于具有标志g的匹配模式来说,该属性存储了在字符串中下一次开始检索的位置。它仅与exec()和test()方法配合使用。

      var s = "javascript is not java";
      var r=/a/gi;                                 //正则表达式直接量
      r.exec(s);                                   //第一次执行匹配
      alert(r.lastIndex);                          //返回值为2
      r.exec(s);                                   //第二次执行匹配
      alert(r.lastIndex);                          //返回值为4
      r.exec(s);                                   //第三次执行匹配
      alert(r.lastIndex);                          //返回值为20
      r.exec(s);                                   //第四次执行匹配
      alert(r.lastIndex);                          //返回值为22
      r.exec(s);                                   //第五次执行匹配
      alert(r.lastIndex);                          //返回值为0

在上面示例中,正则表达式r查找字母a。当它首次检测时,发现在第二个位置(即序号为1)有一个字母a,于是lastIndex属性就被设置为2,即开始下一次匹配时的起始位置。当再次调用exec()方法时,就会从lastIndex属性指定的位置开始匹配,依此类推。

【示例3】可以手动改变lastIndex属性值,强迫正则表达式从指定的位置开始执行检测。

      var s = "0123456789";
      var r=/\d/g;                                 //匹配单个数字
      r.lastIndex=5;                               //指定匹配起始位置为5,即从第六个字符开始匹配
      var a=r.exec(s);                             //执行匹配
      alert(a);                                    //返回匹配数字为5

8.6.2 静态属性

正则表达式的静态属性比较特殊,因为它们都有两个名字:长名(全称)和短名(简称,以美元符号开头表示),详细说明如表8-9所示。

表8-9 RegExp的静态属性

【示例1】在本示例中,匹配字符串"JavaScript",不区分大小写。

      var s = "JavaScript, not Javascript";
      var r = /(Java)Script/gi;
      var a=r.exec(s);                             //执行匹配操作
      alert(RegExp.input);                         //返回字符串"JavaScript, not Javascript"
      alert(RegExp.leftContext);                   //返回空字符串,因为第一次匹配操作时,左侧没有内容
      alert(RegExp.rightContext);                  //返回字符串", not Javascript"
      alert(RegExp.lastMatch);                     //返回字符串"JavaScript"
      alert(RegExp.lastParen);                     //返回字符串"Java"

该示例演示了正则表达式的几个静态属性的用法。

input属性实际上存储的是被执行匹配的字符串,即整个字符串"JavaScript, not Javascript"。

leftContext属性存储的是执行第一次匹配之前的子字符串,这里为空,因为在第一次匹配的文本"JavaScript"左侧为空。而rightContext属性存储的是执行第一次匹配之后的子字符串,即为", not Javascript"。

lastMatch属性包含的是第一次匹配的子字符串,即"JavaScript "。

lastParen属性包含的是第一次匹配的分组,即"Java"。

【示例2】如果模式中包含多个分组,则会显示最后一个分组所匹配的字符。

      var r = /(Java)(Script)/gi;
      var a=r.exec(s);                            //执行匹配操作
      alert(RegExp.lastParen);                    //返回字符串"Script",而不再是"Java"

可以使用短名来读取这些属性所包含的值,考虑到这些短名不符合JavaScript语法规范,则必须使用中括号运算符来进行读取操作。不过对于$_属性来说,它符合JavaScript标识符语法规范,因此可以直接使用。

【示例3】针对上面示例也可以这样设计。

      var s = "JavaScript, not Javascript";
      var r = /(Java)(Script)/gi;
      var a = r.exec(s);
      alert(RegExp.$_);                          //返回字符串"JavaScript, not Javascript"
      alert(RegExp["$`"]);                       //返回空字符串
      alert(RegExp["$'"]);                       //返回字符串", not Javascript"
      alert(RegExp["$&"]);                       //返回字符串"JavaScript"
      alert(RegExp["$+"]);                       //返回字符串"Java"

这些属性的值都是动态的,每次执行exec()或test()方法时,所有属性值都会被重新设置。

【示例4】在本示例中,第一次执行匹配和第二次执行匹配时,这些静态属性值都会实时动态更新。

      var s = "JavaScript, not Javascript";
      var r=/Scrip(t)/gi;                        //第一次定义的匹配模式
      var a=r.exec(s);                           //执行第一次匹配
      alert(RegExp.$_);                          //返回字符串"JavaScript, not Javascript"
      alert(RegExp["$`"]);                       //返回字符串"Java"
      alert(RegExp["$'"]);                       //返回字符串", not Javascript"
      alert(RegExp["$&"]);                       //返回字符串"Script"
      alert(RegExp["$+"]);                       //返回字符串"t"
      var r=/Jav(a)/gi;                          //第二次定义的匹配模式
      var a=r.exec(s);                           //执行第二次匹配
      alert(RegExp.$_);                          //返回字符串"JavaScript, not Javascript"
      alert(RegExp["$`"]);                       //返回空字符串
      alert(RegExp["$'"]);                       //返回字符串"Script, not Javascript"
      alert(RegExp["$&"]);                       //返回字符串"Java"
      alert(RegExp["$+"]);                       //返回字符串"a"

通过上面示例可以看出,RegExp静态属性是公共的,对于所有正则表达式对象来说都可以访问。

8.6.3 案例应用

RegExp对象定义了两个用于执行模式匹配操作的方法,如表8-10所示。它们的行为与String对象的正则表达式操作方法类似。例如,RegExp对象的exec()方法与String对象的match()方法操作相似,只不过exec()是以字符串为参数的RegExp对象方法,而match()方法是以正则表达式为参数的String对象方法。在非全局模式下,它们的返回值是相同的。

表8-10 RegExp对象的方法

1.exec()方法

在所有RegExp模式匹配方法和String模式匹配方法中,exec()方法的功能最强大。作为正则表达式的通用匹配方法,比RegExp.test()、String.search()、String.replace()和String.match()都复杂。该方法需要一个参数,用来执行要执行操作的字符串,并返回一个数组,数组中存放的是匹配结果。如果没有找到匹配,返回值为null。

      var s = "javascript";
      var r = /java/g;
      var a=r.exec(s);                                  //返回数组["java"]

exec()方法的工作机制是这样的:当调用方法时,先检索字符串参数,从中获取与正则表达式相匹配的文本。如果找到了匹配的文本,就会返回一个数组;否则,返回null。返回数组的元素具体说明如下。

第0个元素,是与表达式相匹配的文本。

第一个元素,是与正则表达式的第一个子表达式相匹配的文本(如果存在)。

第二个元素,是与正则表达式的第二个子表达式相匹配的文本,依此类推。

返回数组还包含几个属性,具体说明如下。

length:该属性声明的是数组中的元素个数。

index:该属性声明的是匹配文本的第一个字符的位置。

input:该属性包含的就是整个字符串。

当调用非全局模式的正则表达式对象的exec()方法时,返回的数组与调用字符串对象的match()方法返回数组是完全相同的。即它们都将进行检索,并返回上述元素和属性。

当执行全局匹配模式时,exec()的行为就略有变化。这时它定义lastlndex属性,用来指定下一次执行匹配时开始检索字符串的位置。当它找到了与表达式相匹配的文本时,在匹配之后,它将把正则表达式的lastlndex属性设置为下一次匹配执行的第一个字符的位置。这就是说,可以通过反复地调用exec()方法来遍历字符串中的所有匹配文本。当exec()再也找不到匹配的文本时,它将返回null,并且把属性lastlndex重置为0。

【示例1】在本示例中,定义正则表达式直接量,用来匹配字符串s中每个字符。在循环结构的条件表达式中反复执行匹配模式,并根据返回结果的值是否为null作为循环条件。当返回值为null时,说明字符串检测完毕。然后,读取返回数组a中包含的匹配子字符串,并调用该数组的属性index和lastIndex,其中index显示当前匹配子字符串的起始位置,而lastIndex属性显示下一次匹配操作的起始位置。

      var s="javascript";                               //测试使用的字符串直接量
      var r=/\w/g;                                      //匹配模式
      while((a=r.exec(s))! =null){                      //循环执行匹配操作
          alert(a[0]+"\n"+a.index  +"\n"+  r.lastIndex);//显示每次匹配操作是返回的结果数组信息
      }

实际上通过循环结构反复调用exec()方法是唯一获得全局模式的完整模式匹配信息的方法。

提示:无论正则表达式是否是全局模式,exec()方法都会将完整的细节添加到返回数组中。字符串对象的match()方法就不同,它在全局模式下返回的数组中不会包含这么多的细节信息。

2.test()方法

test()方法要比exec()方法简单,它能够检测参数字符串是否包含至少一个与当前正则表达式的匹配,如果包含匹配就返回true,否则就返回false。

【示例2】在下面示例中,方法test()就会返回true,因为在字符串s中包含很多个匹配:

      var s = "javascript";
      var r=/\w/g;                                      //匹配字符
      var b=r.test(s);                                  //返回true

同样使用下面正则表达式也能够匹配,并返回true:

      var r = /javascript/g;
      var b=r.test(s);                                  //返回true

但是如果使用下面这个正则表达式进行匹配,就会返回false,因为在字符串"javascript"中就找不到对应的匹配:

      var r=/\d/g;                                      //匹配数字
      var b=r.test(s);                                  //返回false

调用test()方法等价于调用exec()方法。如果exec()方法返回值不是null,则test()方法将返回true。因此,当一个全局正则表达式调用方法test()时,它的行为与exec()方法相同,即它将从lastIndex属性值指定的位置开始验证字符串,如果发现了匹配,就会将lastIndex属性值设置为紧邻当前匹配字符串后的字符位置。因此,就可以使用test()方法代替exec()方法来遍历字符串,检测匹配的字符。

【示例3】针对exec()方法中的循环遍历匹配,可以这样设计:

      var s="javascript";                           //测试使用的字符串直接量
      var r=/\w/g;                                  //匹配模式
      while(r.test(s)){                             //循环执行匹配验证,如果返回true,则连续执行验证
          alert(RegExp.lastMatch+"\n"+r.lastIndex); //利用静态属性和实例属性,显示当前匹配信息
      }

【拓展】String对象定义了search()、replace()和match()方法,用来支撑正则表达式的模式匹配操作,而RegExp对象支持test()和exec()方法实现模式匹配操作。

对于search()、replace()和match()方法来说,它们返回的匹配信息没有test()和exec()方法详细。例如,test()和exec()方法支持属性lastIndex。而字符串对象的方法只是将lastIndex属性重置为0。如果使用一个全局模式的exec()方法或test()方法来检索多个字符串,那么就必须找到每个字符串中的所有匹配,以便lastIndex属性会被自动重置为0,或者必须明确地将lastIndex属性设置为0。否则,当再次检索一个新字符串时,起始位置可能就是原来的字符串中的一个任意位置,而不一定是字符串的开头。

对于test()和exec()方法来说,只有声明了g标志的正则表达式,才会发生这种特殊的lastIndex属性行为。如果正则表达式对象没有标志g, exec()和test()将忽略它的lastIndex属性。下面列表比较字符串对象和正则表达式包含的6种模式匹配的方法,如表8-11所示。

表8-11 比较各种模式匹配的方法