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

时间:2009-02-06 15:07:08

标签: javascript regex

我正在尝试解析由&&分隔的键=值对组成的网址编码字符串。

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

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

字符串'1111342 = Adam%20Franco& 348572 = Bob%20Jones'的结果将是:

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

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

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

字符串'1111342 = Adam%20Franco& 348572 = Bob%20Jones'的结果将是:

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

虽然我可以在&上拆分字符串并分别拆分每个键/值对,但是有没有办法使用JavaScript的正则表达式支持来匹配模式/(?:&|&)?([^=]+)=([^&]+)/的多次出现,类似于PHP { {1}}功能?

我的目标是通过某种方式获得子匹配的结果,如:

preg_match_all()

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

15 个答案:

答案 0 :(得分:154)

我建议使用替代正则表达式,使用子组分别捕获参数的名称和值:

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

答案 1 :(得分:65)

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

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

答案 2 :(得分:38)

如果你不想依赖于运行exec样式匹配所带来的“盲目匹配”,那么JavaScript确实内置了全匹配功能,但它是replace的一部分函数调用时,使用“如何处理捕获组”handling function

var data = {};

var getKeyValue = function(fullPattern, group1, group2, group3) {
  data[group2] = group3;
};

mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);

进行。

而不是使用捕获组处理函数来实际返回替换字符串(对于替换处理,这里称为a的第一个arg是完整模式匹配,后续args是单独的捕获组,在这种情况下b是第1组,c第2组等等)我们只需捕获第2组和第3组,然后缓存该对。

因此,不要编写复杂的解析函数,请记住JavaScript中的“matchAll”函数只是用替换处理函数“替换”,并且可以获得很多模式匹配效率。

答案 3 :(得分:21)

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

<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>

答案 4 :(得分:15)

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

/…/g

答案 5 :(得分:11)

来源:https://developer.mozilla.org/ko/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属性。还要确保设置全局标志或在此处也会出现循环。

答案 6 :(得分:2)

如果有人(像我一样)需要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(之前仅返回:事物)

答案 7 :(得分:2)

为了坚持标题所示的建议问题,您实际上可以使用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
  }
]

为了匹配与preg_match_all中的PHP类似的多次出现,你可以使用这种思维来制作自己的或使用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;
}

答案 8 :(得分:1)

如果您可以使用map,这是一个四线解决方案:

&#13;
&#13;
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);
&#13;
&#13;
&#13;

Ain不错,效率很高,但至少它很紧凑。 ;)

答案 9 :(得分:1)

使用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"]]

答案 10 :(得分:1)

从2020年开始的海洛。让我引起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

答案 11 :(得分:0)

嗯......我有类似的问题...... 我想用RegExp进行增量/步骤搜索 (例如:开始搜索...进行一些处理......继续搜索直到最后一场比赛)

经过大量的互联网搜索......总是如此(现在这已成为一种习惯) 我最终在StackOverflow中找到答案......

什么不提及,值得一提的是“lastIndex” 我现在明白为什么RegExp对象实现了“lastIndex”属性

答案 12 :(得分:0)

为了使用相同的名称捕获几个参数,我在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)

拆分看起来像是我的最佳选择:

'1111342=Adam%20Franco&348572=Bob%20Jones'.split('&').map(x => x.match(/(?:&|&amp;)?([^=]+)=([^&]+)/))

答案 14 :(得分:0)

为避免正则表达式陷入困境,您可以找到第一个匹配项,将其切成小块,然后尝试在子字符串中找到下一个匹配项。在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;