Comment compter l'occurrence de chaîne dans une chaîne?


609

Comment puis-je compter le nombre de fois qu'une chaîne particulière se produit dans une autre chaîne. Par exemple, voici ce que j'essaie de faire en Javascript:

var temp = "This is a string.";
alert(temp.count("is")); //should output '2'

19
Cela dépend si vous acceptez les instances qui se chevauchent , par exemple var t = "sss"; Combien d'instances de la sous-chaîne "ss" se trouvent dans la chaîne ci-dessus? 1 ou 2? Sautez-vous par dessus chaque instance ou déplacez-vous le pointeur caractère par caractère, à la recherche de la sous-chaîne?
Tim

4
Un benchmark amélioré pour les réponses à cette question: jsperf.com/string-ocurrence-split-vs-match/2 (basé sur le benchmark de Kazzkiq).
idmean

Réponses:


1030

Le gdans l'expression régulière (abréviation de global ) dit de rechercher la chaîne entière plutôt que de simplement trouver la première occurrence. Cela correspond isdeux fois:

var temp = "This is a string.";
var count = (temp.match(/is/g) || []).length;
console.log(count);

Et, s'il n'y a pas de correspondance, il renvoie 0:

var temp = "Hello World!";
var count = (temp.match(/is/g) || []).length;
console.log(count);


3
moderne et élégant, mais la solution de Vitimtk est beaucoup plus efficace. que pensez-vous tous de son code?
TruMan1

5
Cela répond le mieux à la question. Si quelqu'un demandait "Comment faire 10 fois plus vite dans un cas spécial (sans regexps)" Vitimtk gagnerait cette question.
Dzhaughn

121
Merci pour cela .. Je suis allé count = (str.match(/is/g) || []).lengthgérer si vous n'avez pas de match.
Matt

6
Je ne pense pas que cette réponse corresponde correctement à la question, car elle ne prend pas une chaîne comme argument pour correspondre, comme le décrit le cas d'utilisation. Bien sûr, vous pouvez créer dynamiquement l'expression rationnelle à l'aide du RegExpconstructeur et en passant la chaîne que vous recherchez, mais dans ce cas, vous devez échapper à tous les métacaractères. Dans ce scénario, une approche de chaîne pure est préférable.
ZER0

3
La réponse de Matt devrait être dans la réponse!
Senči

240
/** Function that count occurrences of a substring in a string;
 * @param {String} string               The string
 * @param {String} subString            The sub string to search for
 * @param {Boolean} [allowOverlapping]  Optional. (Default:false)
 *
 * @author Vitim.us https://gist.github.com/victornpb/7736865
 * @see Unit Test https://jsfiddle.net/Victornpb/5axuh96u/
 * @see http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string/7924240#7924240
 */
function occurrences(string, subString, allowOverlapping) {

    string += "";
    subString += "";
    if (subString.length <= 0) return (string.length + 1);

    var n = 0,
        pos = 0,
        step = allowOverlapping ? 1 : subString.length;

    while (true) {
        pos = string.indexOf(subString, pos);
        if (pos >= 0) {
            ++n;
            pos += step;
        } else break;
    }
    return n;
}

Usage

occurrences("foofoofoo", "bar"); //0

occurrences("foofoofoo", "foo"); //3

occurrences("foofoofoo", "foofoo"); //1

allowOverlapping

occurrences("foofoofoo", "foofoo", true); //2

Allumettes:

  foofoofoo
1 `----´
2    `----´

Test de l'unité

Référence

J'ai fait un test de référence et ma fonction est plus de 10 fois plus rapide que la fonction de correspondance regexp publiée par gumbo. Dans ma chaîne de test, la longueur est de 25 caractères. avec 2 occurrences du caractère 'o'. J'ai exécuté 1 000 000 fois dans Safari.

Safari 5.1

Benchmark> Total time execution: 5617 ms (regexp)

Benchmark> Durée totale d'exécution: 881 ms (ma fonction 6,4x plus rapide)

Firefox 4

Benchmark> Durée totale d'exécution: 8547 ms (Rexexp)

Benchmark> Temps d'exécution total: 634 ms (ma fonction 13,5x plus rapide)


Modifier: les modifications que j'ai apportées

  • longueur de sous-chaîne mise en cache

  • ajout de transtypage à la chaîne.

  • paramètre facultatif «allowOverlapping» ajouté

  • correction de la sortie correcte pour le "" cas de sous-chaîne vide.

Essentiel

5
J'ai répété ce test dans Safari 5 et j'ai obtenu des résultats similaires avec une petite chaîne (100b), mais avec une chaîne plus grande (16kb), l'expression régulière a fonctionné plus rapidement pour moi. Pour une itération (pas 1 000 000), la différence était de toute façon inférieure à une milliseconde, donc mon vote va à l'expression régulière.
arlomedia

2
+1, mais vous vérifiez substring.lengthpresque toutes les boucles, vous devriez envisager de le mettre en cache en dehors dewhile
ajax333221

1
@ ajax333221 OMG vous avez lu mon esprit, j'ai fait cette amélioration il y a quelques jours, et j'allais modifier ma réponse jsperf.com/count-string-occurrence-in-string
Vitim.us

4
J'ai trouvé votre code en cours d'utilisation ici: success-equation.com/mind_reader.html . Vraiment sympa le programmeur a pensé y mettre une référence.
Bruno Kim

3
@DanielZuzevich, il contraindra les types à String , au cas où vous le feriez occurrences(11,1) //2et cela fonctionnerait toujours. (Il est plus rapide de procéder de cette façon au lieu de vérifier les types et d'appeler toString () )
Vitim.us

112
function countInstances(string, word) {
   return string.split(word).length - 1;
}

4
Ceci est une approche dangereuse / imprécise, par exemple: countInstances("isisisisisis", "is") === 0.
Nick Craver

5
@Antal - Ressemble à un bogue dans la précédente version bêta de Chrome, fonctionne après la mise à jour au plus tard, je resterais cependant à l'écart de cette méthode.
Nick Craver

28
Cela ressemble à une solution parfaitement valable pour moi.
Gregor Schmidt

2
@NickCraver par curiosité, pourquoi voulez-vous éviter cette méthode? (autre qu'un bug dans votre navigateur bêta)
Jonny Lin

6
@JonnyLin, il crée des allocations inutiles que vous jetez immédiatement lorsque les alternatives ne le font pas - potentiellement très grandes en fonction des données.
Nick Craver

88

Vous pouvez essayer ceci:

var theString = "This is a string.";
console.log(theString.split("is").length - 1);


14
+1 pour la simplicité et parce que selon mes tests cette solution tourne ~ 10x plus vite que les autres!
Claudio Holanda

Par exemple, j'ai deux "est" comment obtenez-vous la position de chacun?
rapidoodle

Comme indiqué dans la réponse de @Orbit, les gens obtiennent des résultats différents sur les anciennes versions de Chrome. Je serais peut-être un peu prudent en utilisant cette méthode.
mgthomas99

Et vous pouvez également l'utiliser avec des variables: theString.split(myvar).length - 1que vous ne pouvez pas utiliser avec des expressions rationnelles simples
Steffan

4
C'est la réponse de @Orbit trois ans plus tard ...
aloisdg passe à codidact.com

33

Ma solution:

var temp = "This is a string.";

function countOcurrences(str, value) {
  var regExp = new RegExp(value, "gi");
  return (str.match(regExp) || []).length;
}

console.log(countOcurrences(temp, 'is'));


5
il serait peut-être préférable de renvoyer (str.match (regExp) || []). length; De cette façon, vous n'évaluez pas l'expression régulière deux fois?
aikeru

2
vous devez également scape votre chaîne ou countOcurrences('Hello...','.')==8pas 3
Vitim.us

19

Vous pouvez utiliser matchpour définir une telle fonction:

String.prototype.count = function(search) {
    var m = this.match(new RegExp(search.toString().replace(/(?=[.\\+*?[^\]$(){}\|])/g, "\\"), "g"));
    return m ? m.length:0;
}

1
Si vous vouliez qu'il soit uniforme avec la sémantique de recherche de JS, la ligne de retour le serait return m ? m.length:-1;.
Conor O'Brien

C'est mieux que les autres solutions de regex ci-dessus, car elles provoquent une erreur si la chaîne pour compter les occurrences de est "[" ou n'importe quoi avec une signification spéciale dans Regex.
programmer5000

11

La version non regex:

 var string = 'This is a string',
    searchFor = 'is',
    count = 0,
    pos = string.indexOf(searchFor);

while (pos > -1) {
    ++count;
    pos = string.indexOf(searchFor, ++pos);
}

console.log(count);   // 2


1. C'est seulement pour la recherche d'un seul caractère, trop subtil 2. même OP demande des isoccurrences
vladkras

1
C'est probablement l'implémentation la plus rapide ici, mais elle serait encore plus rapide si vous remplaçiez "++ pos" par "pos + = searchFor.length"
hanshenrik



8

Voici la fonction la plus rapide!

Pourquoi est-ce plus rapide?

  • Ne vérifie pas caractère par caractère (avec 1 exception)
  • Utilise un certain temps et incrémente 1 var (le nombre de caractères var) par rapport à a pour vérifier la longueur et incrémenter 2 var (généralement var i et un var avec le nombre de caractères)
  • Utilise WAY moins de vars
  • N'utilise pas regex!
  • Utilise une fonction (espérons-le) hautement optimisée
  • Toutes les opérations sont aussi combinées que possible, évitant les ralentissements dus à plusieurs opérations

    String.prototype.timesCharExist=function(c){var t=0,l=0,c=(c+'')[0];while(l=this.indexOf(c,l)+1)++t;return t};

Voici une version plus lente et plus lisible:

    String.prototype.timesCharExist = function ( chr ) {
        var total = 0, last_location = 0, single_char = ( chr + '' )[0];
        while( last_location = this.indexOf( single_char, last_location ) + 1 )
        {
            total = total + 1;
        }
        return total;
    };

Celui-ci est plus lent à cause du compteur, des noms de var longs et de la mauvaise utilisation de 1 var.

Pour l'utiliser, vous faites simplement ceci:

    'The char "a" only shows up twice'.timesCharExist('a');

Modifier: (2013/12/16)

NE PAS utiliser avec Opera 12.16 ou plus! cela prendra presque 2,5 fois plus que la solution regex!

Sur Chrome, cette solution prendra entre 14 ms et 20 ms pour 1 000 000 de caractères.

La solution d'expression régulière prend 11-14 ms pour le même montant.

L'utilisation d'une fonction (à l'extérieur String.prototype) prendra environ 10 à 13 ms.

Voici le code utilisé:

    String.prototype.timesCharExist=function(c){var t=0,l=0,c=(c+'')[0];while(l=this.indexOf(c,l)+1)++t;return t};

    var x=Array(100001).join('1234567890');

    console.time('proto');x.timesCharExist('1');console.timeEnd('proto');

    console.time('regex');x.match(/1/g).length;console.timeEnd('regex');

    var timesCharExist=function(x,c){var t=0,l=0,c=(c+'')[0];while(l=x.indexOf(c,l)+1)++t;return t;};

    console.time('func');timesCharExist(x,'1');console.timeEnd('func');

Le résultat de toutes les solutions devrait être de 100 000!

Remarque: si vous voulez que cette fonction compte plus de 1 char, changement où est c=(c+'')[0]enc=c+''


1
le prototype était UN EXEMPLE! Vous pouvez utiliser la fonction à votre guise! Vous pouvez même le faire: var timesFunctionExist = function (x, c) {var t = 0, l = 0, c = (c + '') [0]; while (l = x.indexOf (c, l) +1 ) ++ t; return t}); alert (timesCharExist ('Le caractère "a" n'apparaît que deux fois', 'a'));! (cela accélérera un peu plus car je ne jouerai pas avec les prototypes). Si vous pensez que je me trompe, pourquoi ne le montrez-vous pas avant de me lancer des pierres? Prouvez-moi que ma fonction est nulle et je l'accepterai. Montrez-moi un cas de test. Et la longueur des vars a une influence sur la vitesse. Vous pouvez le tester.
Ismael Miguel


4

Je pense que le but de regex est très différent de indexOf. indexOftrouvez simplement l'occurrence d'une certaine chaîne tandis qu'en regex vous pouvez utiliser des caractères génériques comme [A-Z]ce qui signifie qu'il trouvera tout caractère majuscule dans le mot sans indiquer le caractère réel.

Exemple:

 var index = "This is a string".indexOf("is");
 console.log(index);
 var length = "This is a string".match(/[a-z]/g).length;
 // where [a-z] is a regex wildcard expression thats why its slower
 console.log(length);


3

Super duper old, mais j'avais besoin de faire quelque chose comme ça aujourd'hui et je ne pensais qu'à vérifier SO après. Fonctionne assez rapidement pour moi.

String.prototype.count = function(substr,start,overlap) {
    overlap = overlap || false;
    start = start || 0;

    var count = 0, 
        offset = overlap ? 1 : substr.length;

    while((start = this.indexOf(substr, start) + offset) !== (offset - 1))
        ++count;
    return count;
};

3
       var myString = "This is a string.";
        var foundAtPosition = 0;
        var Count = 0;
        while (foundAtPosition != -1)
        {
            foundAtPosition = myString.indexOf("is",foundAtPosition);
            if (foundAtPosition != -1)
            {
                Count++;
                foundAtPosition++;
            }
        }
        document.write("There are " + Count + " occurrences of the word IS");

Voir: - compter une sous-chaîne apparaît dans la chaîne pour une explication étape par étape.


3

S'appuyant sur la réponse de @ Vittim.us ci-dessus. J'aime le contrôle que sa méthode me donne, le rendant facile à étendre, mais je devais ajouter une insensibilité à la casse et limiter les correspondances à des mots entiers avec un support pour la ponctuation. (par exemple "bain" signifie "prendre un bain" mais pas "bain")

Le regex de ponctuation provient de: https://stackoverflow.com/a/25575009/497745 ( Comment puis-je supprimer toute la ponctuation d'une chaîne en JavaScript en utilisant regex? )

function keywordOccurrences(string, subString, allowOverlapping, caseInsensitive, wholeWord)
{

    string += "";
    subString += "";
    if (subString.length <= 0) return (string.length + 1); //deal with empty strings

    if(caseInsensitive)
    {            
        string = string.toLowerCase();
        subString = subString.toLowerCase();
    }

    var n = 0,
        pos = 0,
        step = allowOverlapping ? 1 : subString.length,
        stringLength = string.length,
        subStringLength = subString.length;

    while (true)
    {
        pos = string.indexOf(subString, pos);
        if (pos >= 0)
        {
            var matchPos = pos;
            pos += step; //slide forward the position pointer no matter what

            if(wholeWord) //only whole word matches are desired
            {
                if(matchPos > 0) //if the string is not at the very beginning we need to check if the previous character is whitespace
                {                        
                    if(!/[\s\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&\(\)*+,\-.\/:;<=>?@\[\]^_`{|}~]/.test(string[matchPos - 1])) //ignore punctuation
                    {
                        continue; //then this is not a match
                    }
                }

                var matchEnd = matchPos + subStringLength;
                if(matchEnd < stringLength - 1)
                {                        
                    if (!/[\s\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&\(\)*+,\-.\/:;<=>?@\[\]^_`{|}~]/.test(string[matchEnd])) //ignore punctuation
                    {
                        continue; //then this is not a match
                    }
                }
            }

            ++n;                
        } else break;
    }
    return n;
}

N'hésitez pas à modifier et à refactoriser cette réponse si vous détectez des bugs ou des améliorations.


3

Pour tous ceux qui trouveront ce fil à l'avenir, notez que la réponse acceptée ne retournera pas toujours la valeur correcte si vous la généralisez, car elle étouffera les opérateurs regex comme $et .. Voici une meilleure version, qui peut gérer n'importe quelle aiguille:

function occurrences (haystack, needle) {
  var _needle = needle
    .replace(/\[/g, '\\[')
    .replace(/\]/g, '\\]')
  return (
    haystack.match(new RegExp('[' + _needle + ']', 'g')) || []
  ).length
}

3

function get_occurrence(varS,string){//Find All Occurrences
        c=(string.split(varS).length - 1);
        return c;
    }
    temp="This is a string.";
    console.log("Total Occurrence is "+get_occurrence("is",temp));

Utilisez get_occurrence (varS, chaîne) pour rechercher l'occurrence des caractères et de la chaîne dans une chaîne.


2

Essayez-le

<?php 
$str = "33,33,56,89,56,56";
echo substr_count($str, '56');
?>

<script type="text/javascript">
var temp = "33,33,56,89,56,56";
var count = temp.match(/56/g);  
alert(count.length);
</script>


2

Personne ne le verra jamais, mais il est bon de ramener de temps en temps les fonctions de récursivité et de flèche (jeu de mots glorieusement destiné)

String.prototype.occurrencesOf = function(s, i) {
 return (n => (n === -1) ? 0 : 1 + this.occurrencesOf(s, n + 1))(this.indexOf(s, (i || 0)));
};


1

Maintenant, c'est un très vieux fil que j'ai rencontré mais comme beaucoup ont poussé leurs réponses, voici le mien dans l'espoir d'aider quelqu'un avec ce code simple.

var search_value = "This is a dummy sentence!";
var letter = 'a'; /*Can take any letter, have put in a var if anyone wants to use this variable dynamically*/
letter = letter && "string" === typeof letter ? letter : "";
var count;
for (var i = count = 0; i < search_value.length; count += (search_value[i++] == letter));
console.log(count);

Je ne sais pas si c'est la solution la plus rapide mais je l'ai préférée pour la simplicité et pour ne pas utiliser regex (je n'aime tout simplement pas les utiliser!)


1

Cette fonction renvoie le nombre d'occurrences d'un mot dans le texte.

Notez que nous utilisons toLowerCase pour calculer le nombre d'occurrences quel que soit le format (majuscule, majuscule ...) du mot et du texte

wordCount(text, word) {
    if (!text || !word) {
      return 0;
    }
    text = text.toLowerCase();
    word = word.toLowerCase();
    return ( text.split( word ).length - 1 );
}

0

Réponse pour Leandro Batista: juste un problème avec l'expression regex.

 "use strict";
 var dataFromDB = "testal";
 
  $('input[name="tbInput"]').on("change",function(){
	var charToTest = $(this).val();
	var howManyChars = charToTest.length;
	var nrMatches = 0;
	if(howManyChars !== 0){
		charToTest = charToTest.charAt(0);
		var regexp = new RegExp(charToTest,'gi');
		var arrMatches = dataFromDB.match(regexp);
		nrMatches = arrMatches ? arrMatches.length : 0;
	}
		$('#result').html(nrMatches.toString());

  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main">
What do you wanna count <input type="text" name="tbInput" value=""><br />
Number of occurences = <span id="result">0</span>
</div>


0

var countInstances = function(body, target) {
  var globalcounter = 0;
  var concatstring  = '';
  for(var i=0,j=target.length;i<body.length;i++){
    concatstring = body.substring(i-1,j);
    
    if(concatstring === target){
       globalcounter += 1;
       concatstring = '';
    }
  }
  
  
  return globalcounter;
 
};

console.log(   countInstances('abcabc', 'abc')   ); // ==> 2
console.log(   countInstances('ababa', 'aba')   ); // ==> 2
console.log(   countInstances('aaabbb', 'ab')   ); // ==> 1


0

Un peu en retard mais, en supposant que nous ayons la chaîne suivante:

var temp = "This is a string.";

Nous divisons d'abord tout ce que vous cherchez à faire correspondre, cela renverra un tableau de chaînes.

var array = temp.split("is");

Ensuite, nous obtenons sa longueur et lui soustrayons 1, car split par défaut est un tableau de taille 1 et, par conséquent, augmente sa taille à chaque fois qu'il trouve une occurrence.

var occurrenceCount = array.length - 1;
alert(occurrenceCount); //should output '2'

Vous pouvez également faire tout cela en une seule ligne comme suit:

alert("This is a string.".split("is").length - 1); //should output '2'

J'espère que cela aide: D


1
Puis-je signaler cela comme une réponse en double? Peut-être devriez-vous lire toutes les réponses avant de fournir les vôtres?
Michiel

2
Voici la réponse de @Orbit huit ans plus tard ...
aloisdg passe à codidact.com

1
Dois-je alors supprimer cette réponse?
Juan Enrique Segebre

0

Cette solution est basée sur la .replace()méthode qui accepte un RegEx comme premier paramètre et une fonction comme deuxième paramètre que nous pouvons utiliser comme fermeture pour incrémenter un compteur ...

/**
 * Return the frequency of a substring in a string
 * @param {string} string - The string.
 * @param {string} string - The substring to count.
 * @returns {number} number - The frequency.
 * 
 * @author Drozerah https://gist.github.com/Drozerah/2b8e08d28413d66c3e63d7fce80994ce
 * @see https://stackoverflow.com/a/55670859/9370788
 */
const subStringCounter = (string, subString) => {

    let count = 0
    string.replace(new RegExp(subString, 'gi'), () => count++)
    return count
}

Usage

subStringCounter("foofoofoo", "bar"); //0

subStringCounter("foofoofoo", "foo"); //3

0

tombé sur ce post.

let str = 'As sly as a fox, as strong as an ox';

let target = 'as'; // let's look for it

let pos = 0;
while (true) {
  let foundPos = str.indexOf(target, pos);
  if (foundPos == -1) break;

  alert( `Found at ${foundPos}` );
  pos = foundPos + 1; // continue the search from the next position
}

Le même algorithme peut être plus court:

let str = "As sly as a fox, as strong as an ox";
let target = "as";

let pos = -1;
while ((pos = str.indexOf(target, pos + 1)) != -1) {
  alert( pos );
}

0

substr_count traduit en Javascript depuis php


function substr_count (haystack, needle, offset, length) { 
  // eslint-disable-line camelcase
  //  discuss at: https://locutus.io/php/substr_count/
  // original by: Kevin van Zonneveld (https://kvz.io)
  // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman)
  // improved by: Brett Zamir (https://brett-zamir.me)
  // improved by: Thomas
  //   example 1: substr_count('Kevin van Zonneveld', 'e')
  //   returns 1: 3
  //   example 2: substr_count('Kevin van Zonneveld', 'K', 1)
  //   returns 2: 0
  //   example 3: substr_count('Kevin van Zonneveld', 'Z', 0, 10)
  //   returns 3: false

  var cnt = 0

  haystack += ''
  needle += ''
  if (isNaN(offset)) {
    offset = 0
  }
  if (isNaN(length)) {
    length = 0
  }
  if (needle.length === 0) {
    return false
  }
  offset--

  while ((offset = haystack.indexOf(needle, offset + 1)) !== -1) {
    if (length > 0 && (offset + needle.length) > length) {
      return false
    }
    cnt++
  }

  return cnt
}

Consultez la traduction de Locutus de la fonction substr_count de Php


-2

Essaye ça:

function countString(str, search){
    var count=0;
    var index=str.indexOf(search);
    while(index!=-1){
        count++;
        index=str.indexOf(search,index+1);
    }
    return count;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.