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

IT技术 javascript regex
2021-01-21 22:19:27

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

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

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 的正则表达式支持来匹配多次出现的/(?:&|&)?([^=]+)=([^&]+)/类似于 PHPpreg_match_all()函数的模式

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

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

或者

[['1111342', 'Adam%20Franco'], ['348572', 'Bob%20Jones']]
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"
  地理编码:“”
  hl:“德”
  即:“UTF8”
  iwloc:“地址”
  ll:“50.116616,8.680573”
  q:“法兰克福”
  sll:“50.106047,8.679886”
  来源:“s_q”
  spn:“0.35972,0.833588”
  sspn:“0.370369,0.833588”
  z:“11”
}

正则表达式分解如下:

(?: # 非捕获组
  \?|& # "?" 或者 ”&”
  (?:amp;)? #(允许“&”,用于错误的 HTML 编码 URL)
) # 结束非捕获组
( # 第 1 组
  [^=&#]+ # 除“=”、“&”或“#”之外的任何字符;至少一次
) # end group 1 - 这将是参数的名称
(?: # 非捕获组
  =?# 一个“=”,可选
  ( # 第 2 组
    [^&#]* # 除“&”或“#”之外的任何字符;任意次数
  ) # end group 2 - 这将是参数的值
) # 结束非捕获组
因为如果我真的对他们的内容感兴趣,我通常只使用普通(即捕获)组。
2021-03-15 22:19:27
这是我所希望的。我在 JavaScript 文档中从未见过提到 exec() 方法如果被多次调用将继续返回下一个结果集。再次感谢您的提示!
2021-03-20 22:19:27
@KnightYoshi 是的。在 JavaScript 中,任何表达式也会产生它自己的结果(就像x = y赋值yx和产生一样y)。当我们将这些知识应用于if (match = re.exec(url)):此 A) 进行赋值时 B) 将 的结果返回re.exec(url)while现在如果没有匹配则re.exec返回null,这是一个假值。所以实际上只要有匹配,循环就会继续。
2021-03-27 22:19:27
这是因为:regular-expressions.info/javascript.html(通读:“如何使用 JavaScript RegExp 对象”)
2021-03-28 22:19:27
这段代码有一个错误:应该删除“while”后面的分号。
2021-04-03 22:19:27

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

var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)
这实际上并没有解决问题:“使用全局标志,'g',将匹配所有出现的,但只返回完全匹配的子字符串,而不是分隔的键和值。”
2021-04-10 22:19:27

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函数调用的一部分,当使用“如何处理捕获组”处理函数

var data = {};

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

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

完毕。

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

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

不知道为什么这个答案的赞成票这么少,但它是这个问题的最佳答案。
2021-03-16 22:19:27
听起来你应该为此在 Stackoverflow 上发布一个问题,而不是试图在评论中解决它。
2021-04-04 22:19:27
我创建了一个新问题:stackoverflow.com/questions/26174122/...
2021-04-05 22:19:27
我有一个字符串something "this one" and "that one"我想将所有双引号字符串放在一个列表中,即 [this one, that one]。到目前为止,mystring.match(/"(.*?)"/)在检测第一个方面效果很好,但我不知道如何针对单个捕获组调整您的解决方案。
2021-04-06 22:19:27
嗨@Mike'Pomax'Kamermans,社区指南特别推荐编辑条目以改进它们,请参阅:stackoverflow.com/help/behavior您的答案的核心非常有帮助,但我发现“记住 matchAll 是替换”的语言不清楚,也不能解释为什么您的代码(不明显)有效。我认为你应该得到当之无愧的代表,所以我编辑了你的答案,而不是用改进的文本复制它。作为这个问题的最初提问者,如果您仍然希望我接受这个答案(和编辑),我很高兴恢复接受。
2021-04-07 22:19:27

对于捕获组,我习惯于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>
@teh_senaus 您需要指定全局修饰符,/g否则运行exec()不会更改当前索引并将永远循环。
2021-03-14 22:19:27
@fdrv 您必须在开始循环之前将 lastIndex 重置为零:this.lastIndex = 0;
2021-03-15 22:19:27
如果我调用验证此代码 myRe.test(str) 然后尝试执行 execAll,它会在第二场比赛中出现,而我们输掉了第一场比赛。
2021-04-05 22:19:27

设置g全局匹配修饰符:

/…/g
这实际上并没有解决问题:“使用全局标志,'g',将匹配所有出现的,但只返回完全匹配的子字符串,而不是分隔的键和值。”
2021-03-16 22:19:27