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

Rebecca Chernoff
fonte
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
Vitim.us
fonte
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;
}
Órbita
fonte
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);

Freezy Ize
fonte
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'));

Gere
fonte
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;
}
quiabo
fonte
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

Faraz Kelhini
fonte
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
9

String.prototype.Count = function (find) {
    return this.split(find).length - 1;
}

console.log("This is a string.".Count("is"));

Isso retornará 2.

Fad Seck
fonte
3
Esta é a resposta da @Orbit seis anos depois ...
aloisdg movendo-se para codidact.com
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+''

Ismael Miguel
fonte
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);

Sunil Garg
fonte
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);

Simm
fonte
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;
};
Jason Larke
fonte
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.

Ranju
fonte
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.

Ayo I
fonte
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
}
Bcherny
fonte
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.

Rahul Ranjan
fonte
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>
Meghendra S Yadav
fonte
2

Versão simples sem regex:

var temp = "This is a string.";

var count = (temp.split('is').length - 1);

alert(count);

Jorge Alberto
fonte
3
Esta é a resposta da @Orbit seis anos depois ...
aloisdg movendo-se para codidact.com
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)));
};
BaseZen
fonte
2

Tente isto

let allData =  "This is a string.";
let searchString = 'is';
let regularExp = new RegExp(searchString, 'g');
let occurArray = allData.match(regularExp);
let count = (occurArray || []).length;
alert(count); 

Link do Fiddle: https://jsfiddle.net/rajaramtt/gn0dtsjc/1/

Raja Rama Mohan Thavalam
fonte
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!)

Tushar Shukla
fonte
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 );
}
Asakkour Soufiane
fonte
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>

PhilMaGeo
fonte
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

Kamal
fonte
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

Juan Enrique Segebre
fonte
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
Drozerah
fonte
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 );
}
Ashok R
fonte
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

Zachary Horton
fonte
-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;
}
Diogo Arenhart
fonte