Compter le nombre de correspondances d'une regex en Javascript

98

Je voulais écrire une regex pour compter le nombre d'espaces / tabulations / nouvelle ligne dans un morceau de texte. J'ai donc écrit naïvement ce qui suit: -

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

Pour des raisons inconnues, il revient toujours 1. Quel est le problème avec la déclaration ci-dessus? Depuis, j'ai résolu le problème avec ce qui suit: -

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

Réponses:

191

tl; dr: compteur de motifs génériques

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

Pour ceux qui sont arrivés ici à la recherche d'un moyen générique de compter le nombre d'occurrences d'un modèle regex dans une chaîne, et ne veulent pas qu'il échoue s'il n'y a aucune occurrence, ce code est ce dont vous avez besoin. Voici une démonstration:

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

Réponse originale

Le problème avec votre code initial est qu'il vous manque l' identifiant global :

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

Sans la gpartie de l'expression régulière, elle ne correspondra qu'à la première occurrence et s'arrêtera là.

Notez également que votre regex comptera deux fois les espaces successifs:

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

Si ce n'est pas souhaitable, vous pouvez le faire:

>>> 'hi  there'.match(/\s+/g).length;
1
Paolo Bergantino
la source
5
Cela fonctionne tant que vous avez au moins un espace dans votre entrée. Sinon, match () retourne de manière ennuyeuse null.
sfink
3
sfink a raison, vous voulez certainement vérifier si match () a renvoyé null:var result = text.match(/\s/g); return result ? result.length : 0;
Gras Double
37
Vous pouvez également vous protéger contre le nul en utilisant cette construction:( str.match(...) || [] ).length
a'r
11

Comme mentionné dans ma réponse précédente , vous pouvez utiliser RegExp.exec()pour parcourir toutes les correspondances et compter chaque occurrence; l'avantage est limité à la mémoire uniquement, car dans l'ensemble, c'est environ 20% plus lent que l'utilisation String.match().

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

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

return count;
Jack
la source
2

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

Weston Ganger
la source
1
Je pense que vous avez mis le || []mauvais endroit, ça devrait être('my string'.match(/\s/g) || []).length
woojoo666
0

C'est certainement quelque chose qui a beaucoup de pièges. Je travaillais avec la réponse de Paolo Bergantino et je me rendais compte que même cela a des limites. J'ai trouvé que travailler avec des représentations sous forme de chaîne de dates était un bon endroit pour trouver rapidement certains des principaux problèmes. Commencez par une chaîne d'entrée comme celle-ci: '12-2-2019 5:1:48.670'

et configurez la fonction de Paolo comme ceci:

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

Je voulais que l'expression régulière soit transmise, afin que la fonction soit plus réutilisable, deuxièmement, je voulais que le paramètre soit une chaîne, afin que le client n'ait pas à créer l'expression régulière, mais simplement correspondre à la chaîne, comme une méthode de classe utilitaire de chaîne standard.

Maintenant, vous pouvez voir ici que je traite des problèmes avec l'entrée. Avec ce qui suit:

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

Je me assure que l'entrée est rien comme le littéral 0, false, undefinedou null, dont aucun ne sont des chaînes. Étant donné que ces littéraux ne sont pas dans la chaîne d'entrée, il ne devrait y avoir aucune correspondance, mais cela devrait correspondre '0', qui est une chaîne.

Avec ce qui suit:

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

Je traite du fait que le constructeur RegExp interprètera (je pense, à tort) la chaîne '.'comme la correspondance de tous les caractères\.\

Enfin, parce que j'utilise le constructeur RegExp, je dois lui donner le 'g'drapeau global pour qu'il compte toutes les correspondances, pas seulement la première, similaire aux suggestions d'autres articles.

Je me rends compte que c'est une réponse extrêmement tardive, mais cela pourrait être utile à quelqu'un qui trébuche ici. BTW voici la version 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
la source
-2

et comme ça

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