Como contar a ocorrência de string em string?


609

Como posso contar o número de vezes que uma determinada string ocorre em outra string. Por exemplo, é isso que estou tentando fazer em Javascript:

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

19
Depende se você aceita instâncias sobrepostas , por exemplo, var t = "sss"; Quantas instâncias da substring "ss" estão na string acima? 1 ou 2? Você salta sobre cada instância ou move o ponteiro caractere por caractere, procurando a substring?
Tim

4
Um benchmark aprimorado para as respostas desta pergunta: jsperf.com/string-ocurrence-split-vs-match/2 (com base no benchmark de Kazzkiq).
Idmean 27/05

Respostas:


1030

O gna expressão regular (abreviação de global ) diz para pesquisar a seqüência inteira em vez de apenas encontrar a primeira ocorrência. Isso corresponde isduas vezes:

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

E, se não houver correspondências, ele retornará 0:

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


3
moderno e elegante, mas a solução da Vitimtk é muito mais eficiente. o que vocês acham do código dele?
TruMan1

5
Isso responde melhor à pergunta. Se alguém perguntasse "Como posso fazer isso 10 vezes mais rápido em casos especiais (sem regexps)", o Vitimtk venceria essa pergunta.
Dzhaughn

121
Obrigado por isso .. Eu fui count = (str.match(/is/g) || []).lengthpara lidar se você não tem uma correspondência.
Matt

6
Não acho que essa resposta corresponda adequadamente à pergunta, porque não leva uma string como argumento para corresponder, como o caso de uso descreve. Claro, você pode criar dinamicamente a regexp usando o RegExpconstrutor e passando a string que procura, mas nesse caso você precisa escapar de todos os metacaracteres. Nesse cenário, uma abordagem de cadeia pura é preferível.
ZER0 #

3
A resposta de Matt deve estar na resposta!
Senci

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

Uso

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

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

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

allowOverlapping

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

Fósforos:

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

Teste de unidade

Referência

Fiz um teste de benchmark e minha função é 10 vezes mais rápida que a função de correspondência regexp postada pelo gumbo. Na minha sequência de teste é de 25 caracteres. com 2 ocorrências do caractere 'o'. Eu executei 1 000 000 vezes no Safari.

Safari 5.1

Referência> Tempo total de execução: 5617 ms (regexp)

Referência> Tempo total de execução: 881 ms (minha função 6,4x mais rápida)

Firefox 4

Referência> Tempo total de execução: 8547 ms (Rexexp)

Referência> Tempo total de execução: 634 ms (minha função 13,5x mais rápida)


Editar: alterações que fiz

  • comprimento da substring em cache

  • adição de conversão de tipo à string.

  • adicionado parâmetro opcional 'allowOverlapping'

  • saída correta corrigida para "" caso de substring vazio.

Essência

5
Repeti esse teste no Safari 5 e obtive resultados semelhantes com uma string pequena (100b), mas com uma string maior (16kb), o regex correu mais rápido para mim. Para uma iteração (não 1.000.000), a diferença era menor que um milissegundo, de modo que meu voto foi para a regex.
Arlomedia

2
+1, mas você está verificando substring.lengthquase todos os loops, considere colocá-lo em cache fora dowhile
ajax333221

1
@ ajax333221 OMG você leu minha mente, eu fiz essa melhora de alguns dias atrás, e eu estava indo para editar a minha resposta jsperf.com/count-string-occurrence-in-string
Vitim.us

4
Encontrei seu código em uso aqui: success-equation.com/mind_reader.html . Muito bom o programador se importou em colocar uma referência lá.
Bruno Kim

3
@DanielZuzevich forçará os tipos a String , caso você o faça occurrences(11,1) //2e ainda funcionaria. (É mais rápido fazendo desta forma, em vez de verificação de tipos e chamando toString () )
Vitim.us

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

4
Esta é uma abordagem inseguro / imprecisas, por exemplo: countInstances("isisisisisis", "is") === 0.
Nick Craver

5
@Antal - Parece um bug na versão beta anterior do chrome, funciona após a atualização para a mais recente, mas eu ainda evitaria esse método.
Nick Craver

28
Parece-me uma solução perfeitamente válida.
Gregor Schmidt

2
@NickCraver por curiosidade, por que você deseja evitar esse método? (excepto bug em seu navegador beta)
Jonny Lin

6
O @JonnyLin cria alocações desnecessárias que você joga fora imediatamente quando as alternativas não o fazem - potencialmente muito grandes, dependendo dos dados.
Nick Craver

88

Você pode tentar isso:

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


14
+1 pela simplicidade e, de acordo com os meus testes, esta solução é executada ~ 10x mais rápido que as outras!
Claudio Holanda

Por exemplo, eu tenho dois "é" como você obtém a posição de cada um?
Rapidoodle # 28/16

Conforme discutido na resposta do @Orbit, as pessoas estão obtendo resultados diferentes nas versões mais antigas do Chrome. Talvez eu fosse um pouco cauteloso ao usar esse método.
precisa saber é o seguinte

E você também pode usá-lo com variáveis: theString.split(myvar).length - 1o que você não pode com um simples regex
Steffan

4
Esta é a resposta da @Orbit três anos depois ...
aloisdg movendo-se para codidact.com

33

Minha solução:

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
talvez seja melhor retornar (str.match (regExp) || []). length; Dessa forma, você não avalia a expressão regular duas vezes?
aikeru

2
você também precisa escapar sua corda ou countOcurrences('Hello...','.')==8e não 3
Vitim.us

19

Você pode usar matchpara definir essa função:

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

1
Se você quisesse que fosse uniforme com a semântica de pesquisa de JS, a linha de retorno seria return m ? m.length:-1;.
Conor O'Brien

Isso é melhor do que as outras soluções de regex acima, porque elas causam um erro se a sequência de caracteres para contar as ocorrências de for "[" ou qualquer coisa com um significado especial no Regex.
Programmer5000

11

A versão não 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. É apenas para pesquisa único char, muito sutil 2. Mesmo OP pede isocorrências
vladkras

1
Esta é provavelmente a mais rápida implementação aqui, mas seria ainda mais rápido se você substituiu "++ pos" com "pos + = searchFor.length"
hanshenrik



8

Aqui está a função mais rápida!

Por que é mais rápido?

  • Não verifica char por char (com 1 exceção)
  • Usa um tempo e incrementa 1 var (o char count var) vs. a para loop verificando o comprimento e incrementando 2 vars (geralmente var ie um var com o char count)
  • Usa WAY menos vars
  • Não usa regex!
  • Usa uma função (otimizada) altamente otimizada
  • Todas as operações são o mais combinadas possível, evitando lentidão devido a várias operações

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

Aqui está uma versão mais lenta e legível:

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

Este é mais lento devido ao contador, nomes longos de var e uso indevido de 1 var.

Para usá-lo, basta fazer o seguinte:

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

Edição: (16/12/2013)

NÃO use com o Opera 12.16 ou superior! levará quase 2,5 vezes mais que a solução regex!

No chrome, essa solução leva entre 14ms e 20ms para 1.000.000 caracteres.

A solução regex leva 11-14ms para a mesma quantidade.

Usando uma função (fora String.prototype ) leva cerca de 10 a 13ms.

Aqui está o código usado:

    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');

O resultado de todas as soluções deve ser 100.000!

Nota: se você quiser esta função para contar mais de 1 char, mudança, onde está c=(c+'')[0]emc=c+''


1
o protótipo foi um exemplo! Você pode usar a função como quiser! Você pode até fazer isso: 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 ('O caractere "a" aparece apenas duas vezes', 'a')) ;! (isso vai acelerar um pouco mais, porque eu não vou mexer com protótipos). Se você acha que eu estou errado, por que você não mostra isso antes de jogar pedras em mim? Prove que minha função é péssima e eu a aceito. Mostre-me um caso de teste. E o comprimento dos vars tem influência na velocidade. Você pode testá-lo.
Ismael Miguel

7

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


4

Eu acho que o objetivo da regex é muito diferente indexOf. indexOfbasta encontrar a ocorrência de uma determinada string enquanto em regex você pode usar caracteres curinga, o [A-Z]que significa que ela encontrará qualquer caractere maiúsculo na palavra sem indicar o caractere real.

Exemplo:

 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 de idade, mas eu precisava fazer algo assim hoje e só pensei em verificar o SO depois. Funciona muito rápido para mim.

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");

Consulte: - conte uma substring que aparece na string para obter explicações passo a passo.


3

Com base na resposta @ Vittim.us acima. Gosto do controle que o método dele me fornece, facilitando a extensão, mas eu precisava adicionar insensibilidade a maiúsculas e minúsculas e limitar correspondências a palavras inteiras com suporte para pontuação. (por exemplo, "banho" está em "tome banho", mas não em "banho")

A regex de pontuação veio de: https://stackoverflow.com/a/25575009/497745 ( Como posso retirar toda a pontuação de uma sequência em JavaScript usando 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;
}

Sinta-se à vontade para modificar e refatorar esta resposta se detectar bugs ou melhorias.


3

Para qualquer pessoa que encontre esse encadeamento no futuro, observe que a resposta aceita nem sempre retornará o valor correto se você o generalizar, pois ele engasgará com operadores de expressões regulares como $e .. Aqui está uma versão melhor, que pode lidar com qualquer agulha:

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

Use get_occurrence (varS, string) para localizar a ocorrência dos caracteres e da string em uma String.


2

Tente

<?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

Ninguém nunca verá isso, mas é bom recuperar as funções de recursão e flecha de vez em quando (trocadilho gloriosamente intencional)

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


1

Agora, este é um tópico muito antigo que eu já encontrei, mas, como muitos deles responderam, aqui está o meu, na esperança de ajudar alguém com esse código simples.

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

Não tenho certeza se é a solução mais rápida, mas eu o preferi por simplicidade e por não usar regex (eu simplesmente não gosto de usá-los!)


1

Esta função retorna o número de ocorrências de uma palavra no texto.

Observe que usamos o toLowerCase para calcular o número de ocorrências, independentemente do formato (maiúsculas, maiúsculas ...) da palavra e do texto

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

0

Resposta para Leandro Batista: apenas um problema com a expressão 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

Um pouco tarde, mas, assumindo que temos a seguinte string:

var temp = "This is a string.";

Primeiro, dividimos o que você deseja corresponder, isso retornará uma série de strings.

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

Em seguida, obtemos o comprimento e subtraímos 1, pois os padrões divididos para uma matriz de tamanho 1 e, consequentemente, aumentam seu tamanho sempre que encontrar uma ocorrência.

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

Você também pode fazer tudo isso em uma linha da seguinte maneira:

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

Espero que ajude: D


1
Posso sinalizar isso como uma resposta duplicada? Talvez você deva ler todas as respostas antes de fornecer as suas próprias?
Michiel 29/05

2
Esta é a resposta da @Orbit oito anos depois ...
aloisdg movendo-se para codidact.com

1
Devo excluir esta resposta então?
Juan Enrique Segebre 26/10

0

Esta solução é baseada no .replace()método que aceita um RegEx como primeiro parâmetro e uma função como segundo parâmetro que podemos usar como um fechamento para incrementar um contador ...

/**
 * 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
}

Uso

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

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

0

deparei com este 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
}

O mesmo algoritmo pode ser apresentado mais curto:

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 traduzido para Javascript do 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
}

Confira a função substr_count da tradução do php do Locutus


-2

Tente o seguinte:

function countString(str, search){
    var count=0;
    var index=str.indexOf(search);
    while(index!=-1){
        count++;
        index=str.indexOf(search,index+1);
    }
    return count;
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.