zoukankan      html  css  js  c++  java
  • javascript的exec()和match()方法

    转载自他人的博客,非原创

    彻底领悟javascript中的exec与match方法

    阅读本文之前,请先看下面一道题:

    题目17:Read the following javascript code:

    var someText="web2.0 .net2.0";
    var pattern=/(w+)(d).(d)/g;
    var outCome_exec=pattern.exec(someText);
    var outCome_matc=someText.match(pattern);

    What is outCome_exec[1] and outCome_matc[1]?

    Choice A: true
    Choice B: false
    Choice C: null
    Choice D: Web
    Choice E: Web2.0
    Choice F: undefined
    Choice G: net2.0

    这道题据说是某IT公司的一道笔试题,也是引起我写今天这篇文章的原因,不过题目我稍微修改了一下,如果这道题你答对了,你可以不往下面看了。

    -----------------------------------------------------------------

      javascript中与正则表达式有关的匹配字符串的函数主要有RegExp类的方法exec(string)以及String类的方法match(regex),当然还有一些其他的方法,这里不作讨论,但是可能不少程序员都会混淆exec和match,这里列举二者的重点特性:

    1. exec是正则表达式的方法,而不是字符串的方法,它的参数才是字符串,如下所示:

      var re=new RegExp(/d/);
      re.exec( "abc4def" );

      或者使用perl风格:

      /d/.exec( "abc4def" );

      match才是字符串类提供的方法,它的参数是正则表达式对象,如下用法是正确的:

      "abc4def".match(d);
       
    2. exec和match返回的都是数组

      如果执行exec方法的正则表达式没有分组(没有括号括起来的内容),那么如果有匹配,他将返回一个只有一个元素的数组,这个数组唯一的元素就是该正则表达式匹配的第一个串;如果没有匹配则返回null。

      下面两个alert函数弹出的信息是一样的:

      var str= "cat,hat" ;
      var p=/at/; //没有g属性
      alert(p.exec(str))
      alert(str.match(p))

      都是"at"。在这种场合下exec等价于match。

      但是如果正则表达式是全局匹配(g属性)的,那么以上代码结果不一样了:

      var str= "cat,hat" ;
      var p=/at/g; //注意g属性
      alert(p.exec(str))
      alert(str.match(p))

      分别是
      "at"
      "at,at"。

      因为exec永远只返回第一个匹配,而match在正则指定了g属性的时候,会返回所有匹配。
       
    3. exec如果找到了匹配,而且包含分组的话,返回的数组将包含多个元素,第一个元素是找到的匹配,之后的元素依次为该匹配中的第一、第二...个分组(反向引用)

      如下的代码将弹出"cat2,at":

      var str= "cat2,hat8" ;
      var p=/c(at)d/;
      alert(p.exec(str))

      其中第一个元素是匹配的字符串"cat2",之后的元素是括号中匹配的"at"。
       
    4. match函数在满足如下条件下将越俎代庖,实现和exec一样的功能:

      1、正则表达式中含有分组(括号)
      2、返回唯一的匹配

      且看如下的代码:

      var str= "cat2,hat8" ;
      var p=/c(at)d/;
      alert(p.exec(str))
      alert(str.match(p))

      都将弹出消息"cat2,at",是不是觉得很奇怪呢?

    以下链接提供了一些演示:http://www.webchat.com.cn/exec_match.htm

    现在我们再来回顾文章开头提出的问题:

    var someText= "web2.0 .net2.0" ;
    var pattern=/(w+)(d).(d)/g;
    var outCome_exec=pattern.exec(someText);
    var outCome_matc=someText.match(pattern);

    分析:

    outCome_exec的值:pattern中的g属性对exec函数是没有任何作用的,因此exec将匹配第一个可以匹配的字串“web2.0”,作为其返回数组的第一个元素,另外由于pattern中包含三个分组((w+)、(d)、(d)),因此该数组还将包含三个元素,依次是“web”、“2”、“0”,所以该exec执行后的最终结果是:["web2.0","web","2","0"]

    outCome_matc的值:由于pattern是全局匹配的,因此match匹配了所有可以匹配的字串,因此结果数组的值outCome_matc为["web2.0","net2.0"]。如果pattern没有g属性,那么它将与outCome_exec结果一样,因为符合本文第4小节所描述的条件:有分组且返回唯一匹配!

    总结:

    match是返回所有匹配的字符串合成的数组,但是正则表达式必须指定全局g属性才能返回所有匹配,不指定g属性则会返回一个只有一个元素的数组。

    exec永远返回与第一个匹配相关的信息,其返回数组包括第一个匹配的字串,所有分组的反向引用。

    -------------------------------------------

    某些情况下exec返回的结果和match返回的结果一样:

    var str= "cat,hat" ;
    var p=/at/; //没有g属性
    alert(p.exec(str))
    alert(str.match(p))

    都弹出“at”

    -------------------------------------------

    某些情况下match返回的结果和exec返回的结果一样:

    var str= "cat2,hat8" ;
    var p=/c(at)d/;
    alert(p.exec(str))
    alert(str.match(p))

    都弹出“cat2,at”

    一直以来对exec和match方法的区别有些混淆,今天重看帮助文档(帮助文档中主要依据一些晦涩的例子佐证,无助于理解),然后在百度搜集了下一些介绍文章,其中下面一篇文章(楼兰之风...的《彻底领悟javascript中的exec与match方法》)被多次检索,并在百度搜索第一条。但是看完后,做了些例子,发现作者的结论有些问题,更正下,以避免误导各位筒子们。

    1.微笑在看文章之前之前,引用下这个经典面试题,如果知晓一下题目的答案,那就没有必要往下看了。

    var someText="web2.0 .net2.0";
    var pattern=/(w+)(d).(d)/g;
    var outCome_exec=pattern.exec(someText);
    var outCome_matc=someText.match(pattern);

    What is outCome_exec[1] and outCome_matc[1]? 

    Choice A: true 
    Choice B: false 
    Choice C: null 
    Choice D: Web 
    Choice E: Web2.0 
    Choice F: undefined
    Choice G: net2.0

    思考1分钟ing........

                有些知识没用过,思考两天也没用,所以思考不用太久。碰见这样问题就直接google和百度吧!
    2.思考未果完毕,现在看原文以及文中的结论:

    作者原文如下:

     http://www.cnblogs.com/xiehuiqi220/archive/2008/11/05/1327487.html

    最后作者总结的结论如下:

    a)match是返回所有匹配的字符串合成的数组,但是正则表达式必须指定全局g属性才能返回所有匹配,不指定g属性则会返回一个只有一个元素的数组。

    b)exec永远返回与第一个匹配相关的信息,其返回数组包括第一个匹配的字串,所有分组的反向引用。

    3.发现问题:

    3-1)以上结论是错误的。在使用match方法时,如果不指定g属性,则与RegExp对象的exec方法可以等价,而不是只有一个元素的数组。

    举例:

    var str= "ahi" ;
    var exp=/a(hi)/;

    var arr1 = exp.exec(str);
    var arr2 = str.match(exp);
    alert(arr1);//结果:arr1.length==2;arr1[0]==ahi;arr1[1]==hi;
    alert(arr2);//结果:arr2.length==2;arr2[0]==ahi;arr1[1]==hi;结果同上

    3-2)同时,在js帮助文档中,在执行exec方法时,如果有属性g,将该对象的匹配的开始位置设置到紧接这匹配子串的字符位置,当第二次调用exec时,将从
    lastIndex所指示的字符位置开始检索。利用这个特点可以反复调用exec遍历所有匹配,此时等价于match具有g属性的情况(其实就是将匹配的结果放入Matches 集合中去了)。

    举例如下:

    a)有属性g的情况时,更新了index和lastIndex,对下次检索起到作用:

    function RegExpTest() {
        var src = "The rain in Spain falls mainly in the plain.";
        var re = /(w+)/g; // 创建正则表达式模式。    
        var arr;
        while ((arr = re.exec(src)) != null){
         document.write(arr.index + "-" + RegExp.lastIndex + " " + arr[0]);//此处RegExp.lastIndex和arr.lastIndex均有同样的属性,可以互换。在此注意IE6和7的lastIndex重设置0的bug

        }
    };

    RegExpTest();

    //以上例子可以遍历所匹配的内容。并可得到每个小匹配的index和lastIndex;

    b)如果以上例子没有g的情况,则以上例子,exec方法没有更新RegExp 对象的全局属性(index、lastIndex等),以上例子会陷入死循环,index和lastIndex一直为0和3

    可见属性g在exec过程中可以改变index和lastIndex等的值,以便下一次检索的位置,match方法无此能力。

    4.关于index和lastIndex等属性(帮助中还有leftContext、rightContext、lastMatch、lastParen(最后一个括号),但是这些属性均以index和lastindex为基础)。

    4-1)只读属性。

    如下例子:

        var src = "The rain in Spain falls mainly in the plain.";
        var re = /(w+)/g; // 创建正则表达式模式。   
        var arr;
        arr = re.exec(src);    
        RegExp.lastIndex = 0;
        RegExp.index = 0;
        arr.lastIndex = 0;
        arr.index = 0;

        document.write(arr.index + "-" + arr.lastIndex + " " + arr[0]+"**********"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);

        //结果为0-0 The**********0-3 The。

    究其原因也就是RegExp的属性是只读的,即使js语言的灵活性,可以修任何属性或添加任何属性,均不报语法错误。但是依旧无法RegExp的属性更改,但是arrary对象则是可以更改,但是每次执行一次exec,就会将RegExp.index等属性重新赋值给返回的Arrary对象。

    例如:

    var src = "The rain in Spain falls mainly in the plain.";
    var re = /(w+)/g; // 创建正则表达式模式。 
    var arr;
    arr = re.exec(src); 
    RegExp.lastIndex = 0;
    RegExp.index = 0;
    arr.lastIndex = 0;
    arr.index = 0;

    document.write(arr.index + "-" + arr.lastIndex + " " + arr[0]+"**********"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);

    //执行第二次arr的index属性会被更新,其实是RegExp对象实例在执行exec方法时,更新全局的RegExp.index和arr的index等,在后边会介绍

    arr = re.exec(src);
    document.write("<br/>"+arr.index + "-" + arr.lastIndex + " " + arr[0]+"**********"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);

    //0-0 The**********0-3 The
    //4-8 rain**********4-8 rain

    4-2)不同的RegExp实例对象交叉执行exec时,index、lastIndex等属性互不影响。每次执行exec或者执行String的match方法时,都会给RexExp.index等赋予新值。(这个其实是必须的,只是我在这脑袋一犯浑,给理解错了,主要是因为“RegExp.lastIndex = 0;”可以被赋值,但是取值时,结果又没有改变,让我脑袋混乱了。)

    开始我以为如果两个RegExp对象在交叉执行exec时,可能index等会清零。因为我认为index属性是保存在RegExp的全局静态属性上的。现在发现是保存在具体的RegExp实例上,每次执行exec或者执行String的match方法时,都会给RexExp.index等赋予新值。

    呵呵,这可能是习惯了c和java中类和类实例的想法的人常犯的错误,认为RegExp是个类,RegExp.index是一个类的static属性。这样认为没错,但是他的值是是会在执行exec和String的match方法时,被正则对象更新。

    举例如下:

        var src = "The rain in Spain falls mainly in the plain.";
           
        var re1 = /(w+)/; // 创建正则表达式模式。 
        var re2 = /(w+)/g; // 创建正则表达式模式。 
        var arr;

        arr = re1.exec(src);    
        document.write("R1第一次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);
        
        arr = re2.exec(src);
        document.write("<br/>R2第一次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);
        
        arr = re1.exec(src);
        document.write("<br/>R1第二次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);

        arr = re2.exec(src);
        document.write("<br/>R2第二次执行exec:"+RegExp.index + "-" + RegExp.lastIndex + " " + arr[0]);

    输出的结果如下:

    R1第一次执行exec:0-3 The
    R2第一次执行exec:0-3 The
    R1第二次执行exec:0-3 The
    R2第二次执行exec:4-8 rain

    4-3)String对象的match方法,无法像exec方法那样获取中间查找的对象的index和lastIndex,也就是说是一次性的。即无法得到下一次检索的位置,match方法在设置g属性时,只能获取最后一个检索和index和lastIndex;match在没有设置g属性时,仅仅获得第一个匹配的index和lastIndex。

    举例如下:

    a)

    var src = "The rain in Spain falls mainly in the plain.";
    var re = /w+/g; //有g属性。 
    var i = 0;
    while (i++<10){
        arr = src.match(re);
        document.write(RegExp.index + "-" + RegExp.lastIndex + " " + arr + "<br/>");

    }


    //结果如下:

    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain
    38-43 The,rain,in,Spain,falls,mainly,in,the,plain

    b)

    var src = "The rain in Spain falls mainly in the plain.";
    var re = /w+/; // 无g属性。 
    var i = 0;
    while (i++<10){
        arr = src.match(re);
        document.write(RegExp.index + "-" + RegExp.lastIndex + " " + arr + "<br/>");

    }
    //结果如下:

    0-3 The
    0-3 The
    0-3 The
    0-3 The
    0-3 The
    0-3 The
    0-3 The
    0-3 The
    0-3 The
    0-3 The

    c)

    var src = "The rain in Spain falls mainly in the plain.";
    var re = /w+/g; 
    var i = 0;
    arr = src.match(re);
    while (arr[i]!=null){
        document.write(RegExp.index + "-" + RegExp.lastIndex + " " + arr[i] + "<br/>");
        i++;

    //结果如下:

    38-43 The
    38-43 rain
    38-43 in
    38-43 Spain
    38-43 falls
    38-43 mainly
    38-43 in
    38-43 the
    38-43 plain

    5.最后结论(如有不对,请指正):

    1)exec是RegExp对象方法,match是String对象方法;

    2)如果没有找到结果,则二者都返回null;

    3)只有在正则表达式必须指定全局g属性时,match才能返回所有匹配,否则match与exec方法结果无差异,是等价的;

    4)exec永远返回与第一个匹配相关的信息,其返回数组第一个值是第一个匹配的字串,剩下的是所有分组的反向引用(即子括号的匹配内容);

    5)exec在设置g属性后,虽然匹配结果不受g的影响,返回结果仍然是一个数组(第一个值是第一个匹配到的字符串,以后的为分组匹配内容),但是会改变index和lastIndex等的值,将该对象的匹配的开始位置设置到紧接这匹配子串的字符位置,当第二次调用exec时,将从lastIndex所指示的字符位置开始检索。同样match方法在设置了g属性后,也会改变index和lastIndex的值,但是是一次性的。无法像exec那样能逐过程累积(即将结果放入Matches 集合中去了),因此无法累积获取下一次检索的位置。

    PS:

            最开始那个问题的答案为D和G。你想明白了么?

            以上测试均在ie和firefox中测试过,结果一致。

            以上测试的前提是javascript支持RegExp对象。早期浏览器的javascript引擎未必支持正则对象或者未必支持正则表达式对象的某些属性。

  • 相关阅读:
    Hdu 3666 THE MATRIX PROBLEM(差分约束)
    POJ1201Intervals(差分约束系统)
    hdu3440 House Man 【差分约束系统】
    使用字符流读写数据
    使用带有缓冲的字节流读写数据
    使用字节流读写数据
    字符、字节与编码
    Java IO简介
    文件的简单读写
    遍历文件夹
  • 原文地址:https://www.cnblogs.com/zheng-neng-liang/p/6158706.html
Copyright © 2011-2022 走看看