Count number of matches of a regex in Javascript

I wanted to write a regex to count the number of spaces/tabs/newline in a chunk of text. So I naively wrote the following:-

numSpaces : function(text) {
return text.match(/\s/).length;
}

For some unknown reasons it always returns 1. What is the problem with the above statement? I have since solved the problem with the following:-

numSpaces : function(text) {
return (text.split(/\s/).length -1);
}
135912 次浏览

泛型模式计数器

// THIS IS WHAT YOU NEED
const count = (str) => {
const re = /YOUR_PATTERN_HERE/g
return ((str || '').match(re) || []).length
}

对于那些到达这里寻找一种通用方法来计算字符串中正则表达式模式的出现次数,并且不希望在出现次数为零时失败的人来说,这段代码正是您所需要的。这里有一个示范:

/*
*  Example
*/


const count = (str) => {
const re = /[a-z]{3}/g
return ((str || '').match(re) || []).length
}


const str1 = 'abc, def, ghi'
const str2 = 'ABC, DEF, GHI'


console.log(`'${str1}' has ${count(str1)} occurrences of pattern '/[a-z]{3}/g'`)
console.log(`'${str2}' has ${count(str2)} occurrences of pattern '/[a-z]{3}/g'`)

原始答案

初始代码的问题在于缺少 全局标识符:

>>> 'hi there how are you'.match(/\s/g).length;
4

如果没有正则表达式的 g部分,它将只匹配第一个匹配项并在那里停止。

Also note that your regex will count successive spaces twice:

>>> 'hi  there'.match(/\s/g).length;
2

If that is not desirable, you could do this:

>>> 'hi  there'.match(/\s+/g).length;
1

正如在 我之前的回答中提到的,您可以使用 RegExp.exec()迭代所有匹配并计算每次出现的次数; 其优点仅限于内存,因为总体上比使用 String.match()慢20% 。

var re = /\s/g,
count = 0;


while (re.exec(text) !== null) {
++count;
}


return count;

('my string'.match(/\s/g) || []).length;

这样怎么样

function isint(str){
if(str.match(/\d/g).length==str.length){
return true;
}
else {
return false
}
}
(('a a a').match(/b/g) || []).length; // 0
(('a a a').match(/a/g) || []).length; // 3

Based on https://stackoverflow.com/a/48195124/16777 but fixed to actually work in zero-results case.

这肯定是有很多陷阱的东西。我正在研究保罗•伯甘蒂诺(Paolo Bergantino)的答案,并意识到即便如此,也存在一些局限性。我发现使用日期的字符串表示是快速找到一些主要问题的好地方。像这样从一个输入字符串开始: '12-2-2019 5:1:48.670'

并像这样设置保罗的函数:

function count(re, str) {
if (typeof re !== "string") {
return 0;
}
re = (re === '.') ? ('\\' + re) : re;
var cre = new RegExp(re, 'g');
return ((str || '').match(cre) || []).length;
}

我希望传入正则表达式,这样函数可以更好地重用,其次,我希望参数是一个字符串,这样客户端就不必使用正则表达式,只需要在字符串上进行匹配,就像标准的字符串实用程序类方法一样。

现在,您可以看到我正在处理输入的问题:

if (typeof re !== "string") {
return 0;
}

I am ensuring that the input isn't anything like the literal 0, false, undefined, or null, none of which are strings. Since these literals are not in the input string, there should be no matches, but it should match '0', which is a string.

如下:

re = (re === '.') ? ('\\' + re) : re;

我正在处理的事实是,RegExp 构造函数将(我认为是错误的)将字符串 '.'解释为所有字符匹配器 \.\

最后,因为我使用的是 RegExp 构造函数,所以需要给它一个全局 'g'标志,以便它计算所有匹配,而不仅仅是第一个匹配,类似于其他文章中的建议。

我意识到这是一个非常晚的回答,但它可能有助于某人蹒跚在这里。顺便说一下,下面是打字脚本的版本:

function count(re: string, str: string): number {
if (typeof re !== 'string') {
return 0;
}
re = (re === '.') ? ('\\' + re) : re;
const cre = new RegExp(re, 'g');
return ((str || '').match(cre) || []).length;
}

使用现代语法可以避免创建虚拟数组来计算长度0

const countMatches = (exp, str) => str.match(exp)?.length ?? 0;

必须通过 exp作为 RegExpstr作为 String

这里有一个与@Paolo Bergantino 的答案类似的解决方案,但使用的是现代操作符。

    const matchCount = (str, re) => {
return str?.match(re)?.length ?? 0;
};


// usage
    

let numSpaces = matchCount(undefined, /\s/g);
console.log(numSpaces); // 0
numSpaces = matchCount("foobarbaz", /\s/g);
console.log(numSpaces); // 0
numSpaces = matchCount("foo bar baz", /\s/g);
console.log(numSpaces); // 2

?.就是 可选链接操作符。它允许您将调用链接到您想要的任何深度,而不必担心沿途是否存在未定义/null。把 str?.match(re)想象成

if (str !== undefined && str !== null) {
return str.match(re);
} else {
return undefined;
}

这与保罗 · 伯甘蒂诺的作品略有不同。他们的书是这样写的: (str || '')。这意味着如果 str假的,返回 ''。0是假的。所有文件是假的。在我看来,如果有人把它们作为字符串传递给这个函数,那可能是因为程序员的错误。因此,我宁愿被告知我正在做一些不明智的事情,而不是解决为什么我一直得到一个长度为0的问题。

??就是 无效合并运算符。可以把它想象成 ||,但是要更具体一些。如果 ||的左侧计算结果为 假的,则执行右侧。但是 ??只有在左边未定义或为 null 时才会执行。

请记住,?.length ?? 0中的空结合运算符将返回与使用 ?.length || 0相同的结果。区别在于,如果 length返回0,它不会执行右侧... ... 但是无论您使用 ||还是 ??,结果都是0。

老实说,在这种情况下,我可能会将其更改为 ||,因为更多的 JavaScript 开发人员熟悉该操作符。也许有人可以启发我在这种情况下 ??||的好处,如果存在的话。

最后,我修改了签名,这样函数就可以用于任何正则表达式。

哦,这是打印稿的版本:

    const matchCount = (str: string, re: RegExp) => {
return str?.match(re)?.length ?? 0;
};