代码之家  ›  专栏  ›  技术社区  ›  Adam Franco

如何在JavaScript中使用类似于PHP的preg_match_all()的正则表达式匹配多个匹配项?

  •  165
  • Adam Franco  · 技术社区  · 17 年前

    我正试图解析由键=值对组成的url编码字符串,这些字符串由以下两个字符分隔 & & .

    以下内容将仅匹配第一个匹配项,将键和值分解为单独的结果元素:

    var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/)
    

    字符串'1111342=Adam%20Franco&348572=鲍勃%20Jones’将是:

    ['1111342', 'Adam%20Franco']
    

    使用全局标志'g'将匹配所有匹配项,但只返回完全匹配的子字符串,而不返回分隔的键和值:

    var result = mystring.match(/(?:&|&)?([^=]+)=([^&]+)/g)
    

    字符串'1111342=Adam%20Franco&348572=鲍勃%20Jones’将是:

    ['1111342=Adam%20Franco', '&348572=Bob%20Jones']
    

    虽然我可以把绳子劈开 & 并单独拆分每个键/值对,是否有任何方法使用JavaScript的正则表达式支持来匹配该模式的多次出现 /(?:&|&)?([^=]+)=([^&]+)/ 类似于PHP preg_match_all() 功能?

    我的目标是找到一种将子匹配分开的方法来获得结果,比如:

    [['1111342', '348572'], ['Adam%20Franco', 'Bob%20Jones']]
    

    [['1111342', 'Adam%20Franco'], ['348572', 'Bob%20Jones']]
    
    15 回复  |  直到 17 年前
        1
  •  170
  •   Klesun Gian Marco    6 年前

    从评论中提升

    2020评论:我们现在没有使用正则表达式 URLSearchParams ,它为我们完成了所有这些,所以不再需要自定义代码,更不用说正则表达式了。

    Mike 'Pomax' Kamermans

    此处列出了浏览器支持 https://caniuse.com/#feat=urlsearchparams


    我建议使用另一种正则表达式,使用子组单独捕获参数的名称和值 re.exec() :

    function getUrlParams(url) {
      var re = /(?:\?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
          match, params = {},
          decode = function (s) {return decodeURIComponent(s.replace(/\+/g, " "));};
    
      if (typeof url == "undefined") url = document.location.href;
    
      while (match = re.exec(url)) {
        params[decode(match[1])] = decode(match[2]);
      }
      return params;
    }
    
    var result = getUrlParams("http://maps.google.de/maps?f=q&source=s_q&hl=de&geocode=&q=Frankfurt+am+Main&sll=50.106047,8.679886&sspn=0.370369,0.833588&ie=UTF8&ll=50.116616,8.680573&spn=0.35972,0.833588&z=11&iwloc=addr");
    

    result 是一个对象:

    {
      f: "q"
      geocode: ""
      hl: "de"
      ie: "UTF8"
      iwloc: "addr"
      ll: "50.116616,8.680573"
      q: "Frankfurt am Main"
      sll: "50.106047,8.679886"
      source: "s_q"
      spn: "0.35972,0.833588"
      sspn: "0.370369,0.833588"
      z: "11"
    }
    

    正则表达式分解如下:

    (?:            # non-capturing group
      \?|&         #   "?" or "&"
      (?:amp;)?    #   (allow "&", for wrongly HTML-encoded URLs)
    )              # end non-capturing group
    (              # group 1
      [^=]+      #   any character except "=", "&" or "#"; at least once
    )              # end group 1 - this will be the parameter's name
    (?:            # non-capturing group
      =?           #   an "=", optional
      (            #   group 2
        [^]*     #     any character except "&" or "#"; any number of times
      )            #   end group 2 - this will be the parameter's value
    )              # end non-capturing group
    
        2
  •  68
  •   meouw    17 年前

    您需要使用“g”开关进行全局搜索

    var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)
    
        3
  •  40
  •   Mike 'Pomax' Kamermans    6 年前

    2020年编辑

    使用 URLSearchParams ,因为此作业不再需要任何自定义代码。浏览器可以使用单个构造函数为您完成此操作:

    const str = "1111342=Adam%20Franco&348572=Bob%20Jones";
    const data = new URLSearchParams(str);
    for (pair of data) console.log(pair)
    

    产生

    Array [ "1111342", "Adam Franco" ]
    Array [ "348572", "Bob Jones" ]
    

    因此,没有理由再使用正则表达式了。

    原始答案

    如果你不想依赖跑步带来的“盲配” exec 样式匹配,JavaScript确实内置了匹配所有功能,但它是 replace 函数调用时,使用“如何处理捕获组” handling function :

    var data = {};
    
    var getKeyValue = function(fullPattern, group1, group2, group3) {
      data[group2] = group3;
    };
    
    mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);
    

    完成。

    我们不使用捕获组处理函数来实际返回替换字符串(对于替换处理,第一个参数是完整的模式匹配,后续的参数是单独的捕获组),而是简单地获取组2和3的捕获,并缓存该对。

    因此,与其编写复杂的解析函数,不如记住JavaScript中的“matchAll”函数只是用替换处理程序函数“替换”,这样可以提高模式匹配效率。

        4
  •  21
  •   Aram Kocharyan    14 年前

    对于捕捉群组,我习惯于使用 preg_match_all 在PHP中,我试图在这里复制它的功能:

    <script>
    
    // Return all pattern matches with captured groups
    RegExp.prototype.execAll = function(string) {
        var match = null;
        var matches = new Array();
        while (match = this.exec(string)) {
            var matchArray = [];
            for (i in match) {
                if (parseInt(i) == i) {
                    matchArray.push(match[i]);
                }
            }
            matches.push(matchArray);
        }
        return matches;
    }
    
    // Example
    var someTxt = 'abc123 def456 ghi890';
    var results = /[a-z]+(\d+)/g.execAll(someTxt);
    
    // Output
    [["abc123", "123"],
     ["def456", "456"],
     ["ghi890", "890"]]
    
    </script>
    
        5
  •  14
  •   Gumbo    17 年前

    设置 g 全局匹配的修饰符:

    /…/g
    
        6
  •  12
  •   randers    7 年前

    来源:
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec

    查找连续匹配项

    如果你的正则表达式使用了“g”标志,你可以多次使用exec()方法来查找同一字符串中的连续匹配项。当你这样做时,搜索将从正则表达式的lastIndex属性指定的str子字符串开始(test()也将推进lastIndex属性)。例如,假设您有以下脚本:

    var myRe = /ab*/g;
    var str = 'abbcdefabh';
    var myArray;
    while ((myArray = myRe.exec(str)) !== null) {
      var msg = 'Found ' + myArray[0] + '. ';
      msg += 'Next match starts at ' + myRe.lastIndex;
      console.log(msg);
    }
    

    此脚本显示以下文本:

    Found abb. Next match starts at 3
    Found ab. Next match starts at 912
    

    注意:不要将正则表达式文字(或RegExp构造函数)放在while条件中,否则如果每次迭代时都重置lastIndex属性,导致匹配,它将创建一个无限循环。还要确保设置了全局标志,否则这里也会发生循环。

        7
  •  4
  •   Klesun Gian Marco    6 年前

    2020年的Hllo。让我来 String.prototype.matchAll() 请注意:

    let regexp = /(?:&|&amp;)?([^=]+)=([^&]+)/g;
    let str = '1111342=Adam%20Franco&348572=Bob%20Jones';
    
    for (let match of str.matchAll(regexp)) {
        let [full, key, value] = match;
        console.log(key + ' => ' + value);
    }
    

    输出:

    1111342 => Adam%20Franco
    348572 => Bob%20Jones
    
        8
  •  2
  •   fedu    12 年前

    如果有人(像我一样)需要Tomalak的带数组支持的方法(即多选),它是:

    function getUrlParams(url) {
      var re = /(?:\?|&(?:amp;)?)([^=&#]+)(?:=?([^&#]*))/g,
          match, params = {},
          decode = function (s) {return decodeURIComponent(s.replace(/\+/g, " "));};
    
      if (typeof url == "undefined") url = document.location.href;
    
      while (match = re.exec(url)) {
        if( params[decode(match[1])] ) {
            if( typeof params[decode(match[1])] != 'object' ) {
                params[decode(match[1])] = new Array( params[decode(match[1])], decode(match[2]) );
            } else {
                params[decode(match[1])].push(decode(match[2]));
            }
        }
        else
            params[decode(match[1])] = decode(match[2]);
      }
      return params;
    }
    var urlParams = getUrlParams(location.search);
    

    输入 ?my=1&my=2&my=things

    后果 1,2,things (之前只返回:things)

        9
  •  1
  •   Chris West    10 年前

    为了坚持标题所指示的拟议问题,您实际上可以使用以下命令迭代字符串中的每个匹配 String.prototype.replace() 例如,以下操作就是为了基于正则表达式获取所有单词的数组:

    function getWords(str) {
      var arr = [];
      str.replace(/\w+/g, function(m) {
        arr.push(m);
      });
      return arr;
    }
    
    var words = getWords("Where in the world is Carmen Sandiego?");
    // > ["Where", "in", "the", "world", "is", "Carmen", "Sandiego"]
    

    如果我想获得捕获组,甚至每场比赛的索引,我也可以这样做。以下显示了如何返回每个匹配以及整个匹配、第一个捕获组和索引:

    function getWords(str) {
      var arr = [];
      str.replace(/\w+(?=(.*))/g, function(m, remaining, index) {
        arr.push({ match: m, remainder: remaining, index: index });
      });
      return arr;
    }
    
    var words = getWords("Where in the world is Carmen Sandiego?");
    

    在运行上述程序之后, words 将如下:

    [
      {
        "match": "Where",
        "remainder": " in the world is Carmen Sandiego?",
        "index": 0
      },
      {
        "match": "in",
        "remainder": " the world is Carmen Sandiego?",
        "index": 6
      },
      {
        "match": "the",
        "remainder": " world is Carmen Sandiego?",
        "index": 9
      },
      {
        "match": "world",
        "remainder": " is Carmen Sandiego?",
        "index": 13
      },
      {
        "match": "is",
        "remainder": " Carmen Sandiego?",
        "index": 19
      },
      {
        "match": "Carmen",
        "remainder": " Sandiego?",
        "index": 22
      },
      {
        "match": "Sandiego",
        "remainder": "?",
        "index": 29
      }
    ]
    

    为了匹配与PHP中可用的类似的多个事件,请使用 preg_match_all 你可以用这种思维方式来创造自己的,或者使用类似的东西 YourJS.matchAll() .YourJS或多或少地将此函数定义如下:

    function matchAll(str, rgx) {
      var arr, extras, matches = [];
      str.replace(rgx.global ? rgx : new RegExp(rgx.source, (rgx + '').replace(/[\s\S]+\//g , 'g')), function() {
        matches.push(arr = [].slice.call(arguments));
        extras = arr.splice(-2);
        arr.index = extras[0];
        arr.input = extras[1];
      });
      return matches[0] ? matches : null;
    }
    
        10
  •  1
  •   fboes    9 年前

    如果你能逃脱使用 map 这是一个四行解决方案:

    var mystring = '1111342=Adam%20Franco&348572=Bob%20Jones';
    
    var result = mystring.match(/(&|&amp;)?([^=]+)=([^&]+)/g) || [];
    result = result.map(function(i) {
      return i.match(/(&|&amp;)?([^=]+)=([^&]+)/);
    });
    
    console.log(result);

    不漂亮,效率不高,但至少它很紧凑。 ;)

        11
  •  1
  •   jnnnnn    9 年前

    使用 window.URL :

    > s = 'http://www.example.com/index.html?1111342=Adam%20Franco&348572=Bob%20Jones'
    > u = new URL(s)
    > Array.from(u.searchParams.entries())
    [["1111342", "Adam Franco"], ["348572", "Bob Jones"]]
    
        12
  •  0
  •   ivar    14 年前

    为了使用相同的名称捕获多个参数,我修改了Tomalak方法中的while循环,如下所示:

      while (match = re.exec(url)) {
        var pName = decode(match[1]);
        var pValue = decode(match[2]);
        params[pName] ? params[pName].push(pValue) : params[pName] = [pValue];
      }
    

    输入: ?firstname=george&lastname=bush&firstname=bill&lastname=clinton

    返回: {firstname : ["george", "bill"], lastname : ["bush", "clinton"]}

        13
  •  0
  •   p.s.w.g    13 年前

    好。..我也有类似的问题。.. 我想用RegExp进行增量/步进搜索 (例如:开始搜索…做一些处理…继续搜索直到最后一个匹配)

    经过大量的互联网搜索。..像往常一样(现在这已经成为一种习惯了) 我最终在StackOverflow找到了答案。..

    未提及的事项是“ lastIndex " 我现在明白了为什么RegExp对象实现了“ last索引 “财产

        14
  •  0
  •   pguardiario    8 年前

    在我看来,拆分它是最好的选择:

    '1111342=Adam%20Franco&348572=Bob%20Jones'.split('&').map(x => x.match(/(?:&|&amp;)?([^=]+)=([^&]+)/))
    
        15
  •  0
  •   andrew pate    8 年前

    为了避免正则表达式地狱,你可以找到你的第一个匹配,切掉一个块,然后尝试在子字符串上找到下一个。在C#中,这看起来像这样,很抱歉我没有为你将其移植到JavaScript中。

            long count = 0;
            var remainder = data;
            Match match = null;
            do
            {
                match = _rgx.Match(remainder);
                if (match.Success)
                {
                    count++;
                    remainder = remainder.Substring(match.Index + 1, remainder.Length - (match.Index+1));
                }
            } while (match.Success);
            return count;