web-dev-qa-db-fra.com

Compter le nombre de correspondances d'une expression rationnelle en Javascript

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

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

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

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

tl; dr: compteur de modèles générique

// 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 motif regex dans une chaîne, et ne veulent pas que cela échoue s'il n'y a aucune occurrence, ce code est ce qu'il vous faut. 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'identificateur global :

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

Sans la partie g de l'expression régulière, il ne fera que correspondre à la première occurrence et s'arrêter là.

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

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

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

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

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, car globalement, il est environ 20% plus lent que l'utilisation de String.match() .

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

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

return count;
9
Ja͢ck

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

1
Weston Ganger
(('a a a').match(/b/g) || []).length; // 0
(('a a a').match(/a/g) || []).length; // 3

Basé sur https://stackoverflow.com/a/48195124/16777 mais corrigé pour fonctionner réellement dans le cas zéro résultat.

0
Kev

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

et configurer 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 que je traite des problèmes avec l'entrée. Avec ce qui suit:

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

Je m'assure que l'entrée ne ressemble en rien au littéral 0, false, undefined ou null, dont aucune n'est une chaîne. Étant donné que ces littéraux ne figurent pas dans la chaîne d'entrée, il ne devrait y avoir aucune correspondance, mais '0', qui est une chaîne.

Avec ce qui suit:

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

Je traite avec le fait que le constructeur RegExp interprétera (je pense, à tort) la chaîne '.' comme le matcher tout caractère \.\

Enfin, du fait que j'utilise le constructeur RegExp, je dois lui attribuer l'indicateur global 'g' afin qu'il compte toutes les correspondances, et pas seulement la première, de manière similaire aux suggestions formulées dans d'autres publications.

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;
}
0
Michael Coxon