在Javascript中计算正则表达式的匹配数量

130

我想编写一个正则表达式来计算一段文本中空格/制表符/换行符的数量。所以我天真地编写了以下代码:

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

因为某些未知的原因,它总是返回1。上述语句有什么问题?我随后通过以下方式解决了这个问题:

numSpaces : function(text) { 
    return (text.split(/\s/).length -1); 
}
9个回答

253

简述:通用模式计数器

// 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,它只会匹配第一次出现并停止。

此外,请注意您的正则表达式将两次计算连续空格:

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

如果不想那样做,你可以这样:

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

5
只要您的输入中至少有一个空格,这个方法就能正常工作。否则,match() 方法会令人恼火地返回 null。 - sfink
3
sfink是正确的,你一定要检查match()方法是否返回了null:var result = text.match(/\s/g); return result ? result.length : 0; - Gras Double
39
您可以使用以下结构来防止空值:(str.match(...) || []).length - a'r
('string'.match(/\s/g) || []).length 有什么问题? - João Pimentel Ferreira
@JoãoPimentelFerreira 这里的区别在于:如果 str 为空,则 str.match() 会失败,但 (str || '').match() 不会。 - jkdev

11

正如我在早先回答中提到的那样,您可以使用RegExp.exec()来迭代所有匹配项并计算每个出现次数。它的优点仅限于内存使用,因为整体而言,它比使用String.match()要慢约20%。

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

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

return count;

11

5

这是与@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

?.可选链操作符。它允许您在调用链中深入任意层级,而无需担心路径上是否存在undefined/null。把str?.match(re)想象成

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

这与@Paolo Bergantino略有不同。他们的写法是这样的:(str || '')。这意味着如果str为假值,则返回''。0是假值。document.all也是假值。在我看来,如果有人将它们作为字符串传递给此函数,那可能是由于程序员的错误。因此,我更愿意知道我正在做一些非常规操作,而不是调试为什么我一直得到长度为0。 ??nullish coalescing operator。将其视为更具体的||。如果||的左侧求值结果为假值,则执行右侧操作。但是,??仅在左侧操作数未定义或为空时执行。
请记住,在?.length ?? 0中使用的nullish coalescing operator将返回与使用?.length || 0相同的结果。区别在于,如果length返回0,则它不会执行右侧操作...但无论使用||还是??,结果都将是0。
老实说,在这种情况下,我可能会将其更改为||,因为更多的JavaScript开发人员熟悉那个运算符。如果有任何存在的话,也许有人能启发我在此情况下使用??||的好处。
最后,我更改了函数签名,以便可以将该函数用于任何正则表达式。
哦,这是一个typescript版本:
    const matchCount = (str: string, re: RegExp) => {
      return str?.match(re)?.length ?? 0;
    };

3

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


1
我认为你把 || [] 放错了位置,应该是 ('my string'.match(/\s/g) || []).length - woojoo666

2
这绝对是一个充满陷阱的问题。我正在使用Paolo Bergantino的回答,并意识到即使那个也有一些限制。我发现使用日期的字符串表示是快速找到一些主要问题的好方法。从类似这样的输入字符串开始: '12-2-2019 5:1:48.670' 并像这样设置Paolo的函数:
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;
}

我正在确保输入值不是字面量0falseundefinednull,这些都不是字符串。由于这些字面量不在输入字符串中,所以不应该匹配任何内容,但应该匹配'0',因为它是一个字符串。
以下是代码示例:
re = (re === '.') ? ('\\' + re) : re;

最初的回答: 我正在处理一个问题,即RegExp构造函数会(我认为是错误地)将字符串'.'解释为所有字符匹配器'\.'。最后,由于我在使用RegExp构造函数,我需要给它全局标志'g',以便它计算所有匹配项,而不仅仅是第一个匹配项,与其他帖子中的建议类似。我意识到这是一个非常晚的回答,但它可能对偶然经过这里的人有所帮助。顺便说一句,这是TypeScript版本:
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;
}

2

这个问题似乎已经解决了,但我还没看到这个版本,它可能更易读,并且符合一些代码库的风格。

const numberOfResults = [...str.matchAll(/YOUR_REGEX/g)].length;

0

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

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

必须将exp作为RegExp传递,并将str作为String传递。

-2

这样怎么样?

function isint(str){
    if(str.match(/\d/g).length==str.length){
        return true;
    }
    else {
         return false
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接