我正在尝试解析由由 &
或 &
分隔的 key=value 对组成的 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 的正则表达式支持来匹配模式 /(?:&|&)?([^=]+)=([^&]+)/
的多次出现,类似于 PHP 的 preg_match_all()
函数?
我的目标是通过某种方式获得子匹配分离的结果,例如:
[['1111342', '348572'], ['Adam%20Franco', 'Bob%20Jones']]
或者
[['1111342', 'Adam%20Franco'], ['348572', 'Bob%20Jones']]
replace
,这有点奇怪。 var data = {}; mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, function(a,b,c,d) { data[c] = d; });
完成。 JavaScript 中的“matchAll”是用替换处理函数而不是字符串“替换”。
从评论中提升
2020 年评论:我们现在有 URLSearchParams,而不是使用正则表达式,它为我们完成所有这些,因此不再需要自定义代码,更不用说正则表达式了。 – 迈克“Pomax”卡默曼斯
此处列出了浏览器支持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
您需要使用“g”开关进行全局搜索
var result = mystring.match(/(&|&)?([^=]+)=([^&]+)/g)
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 确实内置了 match-all 功能,但它是 replace
函数调用的一部分,当使用“如何处理捕获组”handling function:
var data = {};
var getKeyValue = function(fullPattern, group1, group2, group3) {
data[group2] = group3;
};
mystring.replace(/(?:&|&)?([^=]+)=([^&]+)/g, getKeyValue);
完毕。
我们不使用捕获组处理函数来实际返回替换字符串(对于替换处理,第一个 arg 是完整的模式匹配,随后的 args 是单独的捕获组),我们只需获取组 2 和 3 捕获,并缓存该对。
因此,不要编写复杂的解析函数,记住 JavaScript 中的“matchAll”函数只是用替换处理函数“替换”,可以提高模式匹配效率。
something "this one" and "that one"
。我想将所有双引号字符串放在一个列表中,即[这个,那个]。到目前为止,mystring.match(/"(.*?)"/)
可以很好地检测第一个,但我不知道如何使您的解决方案适应单个捕获组。
对于捕获组,我习惯于在 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>
/g
指定全局修饰符,否则运行 exec()
不会更改当前索引并将永远循环。
为全局匹配设置 g
修饰符:
/…/g
来源:
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 属性在每次迭代时被重置而存在匹配,它将创建一个无限循环。还要确保设置了全局标志,否则这里也会出现循环。
String.prototype.match
与 g
标志结合使用:'abbcdefabh'.match(/ab*/g)
返回 ['abb', 'ab']
2020 年您好。让我提请您注意 String.prototype.matchAll():
let regexp = /(?:&|&)?([^=]+)=([^&]+)/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
如果有人(像我一样)需要支持数组的 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
(之前仅返回:事物)
只是为了坚持标题所示的建议问题,您实际上可以使用 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;
}
YourJS.parseQS()
(yourjs.com/snippets/56) 之类的东西,尽管许多其他库也提供此功能。
如果您可以使用 map
,这是一个四行解决方案:
var mystring = '1111342=Adam%20Franco&348572=Bob%20Jones'; var 结果 = mystring.match(/(&|&)?([^=]+)=([^&]+)/g) || []; result = result.map(function(i) { return i.match(/(&|&)?([^=]+)=([^&]+)/); });控制台.log(结果);
不漂亮,效率不高,但至少它是紧凑的。 ;)
使用 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"]]
为了使用相同的名称捕获多个参数,我修改了 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"]}
?cinema=1234&film=12&film=34
我希望 {cinema: 1234, film: [12, 34]}
。编辑您的答案以反映这一点。
嗯......我有一个类似的问题......我想要使用 RegExp 进行增量/步进搜索(例如:开始搜索......做一些处理......继续搜索直到最后一个匹配)
经过大量的互联网搜索......像往常一样(现在这已经成为一种习惯)我最终在 StackOverflow 中找到了答案......
什么没有提到,重要的是“lastIndex
”我现在明白了为什么 RegExp 对象实现了“lastIndex
”属性
拆分它对我来说似乎是最好的选择:
'1111342=Adam%20Franco&348572=Bob%20Jones'.split('&').map(x => x.match(/(?:&|&)?([^=]+)=([^&]+)/))
为了避免正则表达式地狱,你可以找到你的第一个匹配,切掉一个块然后尝试在子字符串上找到下一个。在 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;
x = y
会将y
分配给x
并产生y
)。当我们将该知识应用于if (match = re.exec(url))
时: A) 执行赋值 并且 B) 将re.exec(url)
的结果返回给while
。如果没有匹配,现在re.exec
返回null
,这是一个假值。所以实际上只要有匹配,循环就会继续。