当我跑
/(a)/g.exec('a a a ').length
我明白了
2
但我认为它应该返回
3
a
因为字符串中有 3 个,而不是 2 个!
这是为什么?
我希望能够在 RegEx 中搜索所有出现的字符串并对其进行迭代。
FWIW:我正在使用 node.js
当我跑
/(a)/g.exec('a a a ').length
我明白了
2
但我认为它应该返回
3
a
因为字符串中有 3 个,而不是 2 个!
这是为什么?
我希望能够在 RegEx 中搜索所有出现的字符串并对其进行迭代。
FWIW:我正在使用 node.js
exec()
仅返回第一个匹配的捕获集,而不是您期望的匹配集。所以你真正看到的是$0
(整个比赛,“a”)和$1
(第一次捕获)——即一个长度为 2 的数组。 同时被设计为你可以再次exec()
调用它来获取下一场比赛的捕获. 来自MDN:
如果您的正则表达式使用“g”标志,您可以多次使用 exec 方法在同一字符串中查找连续匹配项。当您这样做时,搜索将从正则表达式的 lastIndex 属性指定的 str 的子字符串开始(测试也将推进 lastIndex 属性)。
您可以match
改用:
'a a a'.match(/(a)/g).length // outputs: 3
while循环可以帮助你
x = 'a a a a';
y = new RegExp(/a/g);
while(null != (z=y.exec(x))) {
console.log(z); // output: object
console.log(z[0]); // ouput: "a"
}
如果你添加计数器,那么你会得到它的长度。
x = 'a a a a';
counter = 0;
y = new RegExp(/a/g);
while(null != (z=y.exec(x))) {
console.log(z); // output: object
console.log(z[0]); // output: "a"
counter++;
}
console.log(counter); // output: 4
这是非常安全的,即使它没有找到任何匹配项,它也会退出并且计数器将为 0
主要目的是说明如何使用 RegExp 循环并从相同匹配的 RegExp 字符串中获取所有值
您只匹配第一个 a。长度为 2 的原因是它正在查找第一个匹配项和第一个匹配项的括号组部分。在您的情况下,它们是相同的。
考虑这个例子。
var a = /b(a)/g.exec('ba ba ba ');
alert(a);
它输出ba, a
. 数组长度仍然是 2,但更明显的是发生了什么。"ba" 是完全匹配。 a
是括号内的第一个分组匹配。
MDN 文档支持这一点——只返回第一个匹配项和包含的组。要查找所有匹配项,您将使用 mVChr 所述的 match()。
代码:
alert('a a a'.match(/(a)/g).length);
输出:
3
regexp.exec(str)
返回第一个匹配项或整个匹配项和第一个捕获(何时re = /(a)/g;
),如其他答案中所述
const str = 'a a a a a a a a a a a a a';
const re = /a/g;
const result = re.exec(str);
console.log(result);
但它也会记住它在regexp.lastIndex
属性中的位置。
下一个调用开始搜索regexp.lastIndex
并返回下一个匹配项。
如果没有更多匹配项,则regexp.exec
返回 null 并regexp.lastIndex
设置为 0。
const str = 'a a a';
const re = /a/g;
const a = re.exec(str);
console.log('match : ', a, ' found at : ', re.lastIndex);
const b = re.exec(str);
console.log('match : ', b, ' found at : ', re.lastIndex);
const c = re.exec(str);
console.log('match : ', c, ' found at : ', re.lastIndex);
const d = re.exec(str);
console.log('match : ', d, ' found at : ', re.lastIndex);
const e = re.exec(str);
console.log('match : ', e, ' found at : ', re.lastIndex);
这就是为什么你可以使用一个while循环,当比赛结束时会停止null
const str = 'a a a';
const re = /a/g;
while(match = re.exec(str)){
console.log(match, ' found at : ', match.index);
}
已经有几个答案,但不必要地复杂。对结果的身份检查过多,因为它始终是数组或null
.
let text = `How much wood would a woodchuck chuck if a woodchuck could chuck wood?`;
let re = /wood/g;
let lastMatch;
while (lastMatch = re.exec(text)) {
console.log(lastMatch);
console.log(re.lastIndex);
// Avoid infinite loop
if(!re.global) break;
}
您可以将无限循环保护移动到条件表达式中。
while (re.global && (lastMatch = re.exec(text))) {
console.log(lastMatch);
console.log(re.lastIndex);
}
封装成一个效用函数:
const regexExecAll = (str: string, regex: RegExp) => {
let lastMatch: RegExpExecArray | null;
const matches: RegExpExecArray[] = [];
while ((lastMatch = regex.exec(str))) {
matches.push(lastMatch);
if (!regex.global) break;
}
return matches;
};
用法:
const matches = regexExecAll("a a a", /(a)/g);
console.log(matches);
输出:
[
[ 'a', 'a', index: 0, input: 'a a a', groups: undefined ],
[ 'a', 'a', index: 2, input: 'a a a', groups: undefined ],
[ 'a', 'a', index: 4, input: 'a a a', groups: undefined ]
]
对于您的示例,.match()
是您的最佳选择。但是,如果您确实需要子组,则可以创建一个生成器函数。
function* execAll(str, regex) {
if (!regex.global) {
console.error('RegExp must have the global flag to retrieve multiple results.');
}
let match;
while (match = regex.exec(str)) {
yield match;
}
}
const matches = execAll('a abbbbb no match ab', /\b(a)(b+)?\b/g);
for (const match of matches) {
console.log(JSON.stringify(match));
let otherProps = {};
for (const [key, value] of Object.entries(match)) {
if (isNaN(Number(key))) {
otherProps[key] = value;
}
}
console.log(otherProps);
}
虽然大多数 JS 程序员认为污染原型是不好的做法,但您也可以将其添加到RegExp.prototype
.
if (RegExp.prototype.hasOwnProperty('execAll')) {
console.error('RegExp prototype already includes a value for execAll. Not overwriting it.');
} else {
RegExp.prototype.execAll =
RegExp.prototype = function* execAll(str) {
if (!this.global) {
console.error('RegExp must have the global flag to retrieve multiple results.');
}
let match;
while (match = this.exec(str)) {
yield match;
}
};
}
const matches = /\b(a)(b+)?\b/g.execAll('a abbbbb no match ab');
console.log(Array.from(matches));