Contar o número de correspondências de um regex em Javascript

98

Eu queria escrever um regex para contar o número de espaços / tabulações / nova linha em um pedaço de texto. Então, eu ingenuamente escrevi o seguinte: -

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

Por algumas razões desconhecidas, ele sempre retorna 1. Qual é o problema com a afirmação acima? Desde então, resolvi o problema com o seguinte: -

numSpaces : function(text) { 
    return (text.split(/\s/).length -1); 
}
wai
fonte

Respostas:

191

tl; dr: Contador de padrão genérico

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

Para aqueles que chegaram aqui em busca de uma forma genérica de contar o número de ocorrências de um padrão regex em uma string e não querem que falhe se houver zero ocorrências, esse código é o que você precisa. Aqui está uma demonstração:

/*
 *  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'`)

Resposta Original

O problema com seu código inicial é que falta o identificador global :

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

Sem a gparte da regex, ele apenas corresponderá à primeira ocorrência e parará por aí.

Observe também que sua regex contará espaços sucessivos duas vezes:

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

Se isso não for desejável, você pode fazer o seguinte:

>>> 'hi  there'.match(/\s+/g).length;
1
Paolo Bergantino
fonte
5
Isso funciona desde que você tenha pelo menos um espaço em sua entrada. Caso contrário, match () retorna irritantemente nulo.
afundar em
3
sfink está certo, você definitivamente deseja verificar se match () retornou null:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double
37
Você também pode se proteger contra o nulo usando esta construção:( str.match(...) || [] ).length
a'r
11

Conforme mencionado na minha resposta anterior , você pode usar RegExp.exec()para iterar todas as correspondências e contar cada ocorrência; a vantagem é limitada apenas à memória, porque no geral é cerca de 20% mais lento do que o uso String.match().

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

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

return count;
Ja͢ck
fonte
2

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

Weston Ganger
fonte
1
Acho que você colocou || []no lugar errado, deveria ser('my string'.match(/\s/g) || []).length
woojoo666
0

Isso certamente é algo que tem muitas armadilhas. Eu estava trabalhando com a resposta de Paolo Bergantino, e percebendo que mesmo isso tem algumas limitações. Achei que trabalhar com representações de strings de datas é um bom lugar para encontrar rapidamente alguns dos principais problemas. Comece com uma string de entrada como esta: '12-2-2019 5:1:48.670'

e configurar a função de Paolo assim:

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;
}

Eu queria que a expressão regular fosse passada, para que a função fosse mais reutilizável, em segundo lugar, eu queria que o parâmetro fosse uma string, para que o cliente não tivesse que fazer a regex, mas simplesmente corresponder na string, como um método de classe de utilitário de string padrão.

Agora, aqui você pode ver que estou lidando com problemas com a entrada. Com o seguinte:

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

Eu estou garantindo que a entrada não é nada parecido com o literal 0, false, undefined, ou null, nenhum dos quais são strings. Como esses literais não estão na string de entrada, não deve haver correspondências, mas deve haver correspondência '0', que é uma string.

Com o seguinte:

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

Estou lidando com o fato de que o construtor RegExp irá (eu acho, erroneamente) interpretar a string '.'como o combinador de todos os caracteres\.\

Finalmente, como estou usando o construtor RegExp, preciso dar a ele o 'g'sinalizador global para que conte todas as correspondências, não apenas a primeira, semelhante às sugestões em outras postagens.

Sei que essa é uma resposta extremamente tardia, mas pode ser útil para alguém que está tropeçando aqui. BTW, aqui está a versão do 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;
}
Michael Coxon
fonte
-2

que tal assim

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