Jak policzyć występowanie ciągu w ciągu?


607

Jak mogę policzyć, ile razy dany ciąg występuje w innym ciągu. Na przykład to właśnie próbuję zrobić w Javascript:

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

19
Zależy to od tego, czy akceptujesz nakładające się wystąpienia, np. Var t = "sss"; Ile wystąpień podłańcucha „ss” znajduje się w powyższym ciągu? 1 lub 2? Czy przeskakujesz po każdej instancji, czy przesuwasz wskaźnik znak po znaku, szukając podłańcucha?
Tim

4
Ulepszony test porównawczy dla odpowiedzi na to pytanie: jsperf.com/string-ocurrence-split-vs-match/2 (na podstawie testu porównawczego Kazzkiq).
idmean

Odpowiedzi:


1028

W gwyrażeniu regularnym (skrót od globalnego ) mówi się, aby przeszukać cały ciąg, a nie tylko znaleźć pierwsze wystąpienie. To pasuje isdwa razy:

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

A jeśli nie ma dopasowań, zwraca 0:

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


3
nowoczesne i eleganckie, ale rozwiązanie Vitimtk jest znacznie wydajniejsze. co wszyscy myślicie o jego kodzie?
TruMan1

5
To najlepiej odpowiada na pytanie. Gdyby ktoś zapytał „Jak mogę to zrobić 10 razy szybciej w specjalnych przypadkach (bez wyrażeń regularnych)”, Vitimtk wygrałby to pytanie.
Dzhaughn,

121
Dzięki za to .. Poszedłem count = (str.match(/is/g) || []).lengthzałatwić sprawę, jeśli nie masz dopasowania.
Matt.

6
Nie sądzę, aby ta odpowiedź pasowała poprawnie do pytania, ponieważ nie wymaga ciągu jako argumentu zgodnego z opisem, jak opisują to przypadki użycia. Jasne, możesz dynamicznie tworzyć wyrażenie regularne za pomocą RegExpkonstruktora i przekazując szukany ciąg, ale w takim przypadku musisz uciec od wszystkich metaznaków. W tym scenariuszu preferowane jest podejście oparte na czystym ciągu.
ZER0

3
Odpowiedź Matta powinna być w odpowiedzi!
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;
}

Stosowanie

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

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

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

allowOverlapping

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

Mecze:

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

Test jednostkowy

Reper

Zrobiłem test porównawczy, a moja funkcja jest ponad 10 razy szybsza niż funkcja dopasowania wyrażenia regularnego opublikowana przez gumbo. W moim teście ciąg ma 25 znaków. z 2 wystąpieniami postaci „o”. Wykonałem 1 000 000 razy w Safari.

Safari 5.1

Benchmark> Całkowity czas wykonania: 5617 ms (wyrażenie regularne)

Benchmark> Całkowity czas wykonania: 881 ms (moja funkcja 6,4x szybciej)

Firefox 4

Benchmark> Całkowity czas wykonania: 8547 ms (Rexexp)

Benchmark> Całkowity czas wykonania: 634 ms (moja funkcja 13,5x szybciej)


Edycja: zmiany, które wprowadziłem

  • buforowana długość podciągu

  • dodano rzutowanie tekstu na ciąg.

  • dodano opcjonalny parametr „allowOverlapping”

  • naprawiono poprawne wyjście dla pustej skrzynki na podciąg.

Sens

5
Powtórzyłem ten test w Safari 5 i uzyskałem podobne wyniki z małym ciągiem (100b), ale przy większym ciągu (16kb) regex działał dla mnie szybciej. Dla jednej iteracji (nie 1 000 000) różnica i tak była mniejsza niż milisekunda, więc mój głos trafia do wyrażenia regularnego.
arlomedia,

2
+1, ale sprawdzasz substring.lengthprawie każdą pętlę, powinieneś rozważyć umieszczenie jej w pamięci podręcznej pozawhile
ajax333221,

1
@ ajax333221 OMG czytałeś w moich myślach, zrobiłem to ulepszenie kilka dni temu i miałem zamiar edytować moją odpowiedź jsperf.com/count-string-occurrence-in-string
Vitim.us

4
Znalazłem używany kod tutaj: success-equation.com/mind_reader.html . Naprawdę miło, że programista chciał umieścić tam referencje.
Bruno Kim,

3
@DanielZuzevich zmusi typy do String , na wypadek, gdybyś to zrobił occurrences(11,1) //2i nadal będzie działać. (Robi się to szybciej, zamiast sprawdzać typy i wywoływać
metodę

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

4
Jest to niebezpieczne / niedokładne podejście, na przykład: countInstances("isisisisisis", "is") === 0.
Nick Craver

5
@Antal - Wygląda na błąd w poprzedniej wersji beta chrome, działa po aktualizacji do najnowszej, ale nadal będę unikać tej metody.
Nick Craver

28
Dla mnie wygląda to na całkowicie poprawne rozwiązanie.
Gregor Schmidt

2
@NickCraver z ciekawości, dlaczego chcesz omijać tę metodę? (inny niż błąd w przeglądarce beta)
Jonny Lin

6
@JonnyLin tworzy niepotrzebne przydziały, które natychmiast wyrzucasz, gdy alternatywy tego nie robią - potencjalnie bardzo duże w zależności od danych.
Nick Craver

88

Możesz spróbować:

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


14
+1 za prostotę i ponieważ zgodnie z moimi testami to rozwiązanie działa około 10 razy szybciej niż inne!
Claudio Holanda

Na przykład mam dwa „jest”, jak uzyskać pozycję każdego z nich?
rapidoodle

Jak omówiono w odpowiedzi na @Orbit, ludzie uzyskują różne wyniki w starszych wersjach Chrome. Być może byłbym trochę ostrożny przy użyciu tej metody.
mgthomas99

I możesz go również używać ze zmiennymi: theString.split(myvar).length - 1których nie możesz z prostym wyrażeniem regularnym
Steffan

4
To @Orbit „s odpowiedź trzy lata później ...
aloisdg przeprowadzce do codidact.com

33

Moje rozwiązanie:

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
może lepiej byłoby zwrócić (str.match (regExp) || []). length; W ten sposób nie oceniasz wyrażenia regularnego dwukrotnie?
aikeru

2
musisz również countOcurrences('Hello...','.')==8
zgarnąć

19

Możesz użyć matchdo zdefiniowania takiej funkcji:

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

1
Jeśli chciałbyś, aby był jednolity z semantyką wyszukiwania JS, linia powrotu byłaby return m ? m.length:-1;.
Conor O'Brien,

Jest to lepsze niż w przypadku innych powyższych wyrażeń regularnych, ponieważ powodują one błąd, jeśli ciąg do zliczenia wystąpień ma wartość „[” lub cokolwiek o specjalnym znaczeniu w wyrażeniu regularnym.
programista

11

Wersja inna niż wyrażenia regularne:

 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. To tylko dla jednego wyszukiwania char, zbyt subtelny 2. nawet OP prosi o iswystąpień
vladkras

1
Jest to prawdopodobnie najszybsza implementacja tutaj, ale byłoby jeszcze szybsze, gdyby zastąpić „++ pos” na „pos + = searchFor.length”
hanshenrik



8

Oto najszybsza funkcja!

Dlaczego to jest szybsze?

  • Nie sprawdza char według char (z 1 wyjątkiem)
  • Wykorzystuje chwilę i zwiększa 1 var (char var var) w porównaniu do pętli for sprawdzającej długość i zwiększającej 2 zmienne (zwykle var i i var z char char)
  • Używa WAY mniej wariacji
  • Nie używa wyrażenia regularnego!
  • Wykorzystuje (miejmy nadzieję) wysoce zoptymalizowaną funkcję
  • Wszystkie operacje są tak połączone, jak to tylko możliwe, unikając spowolnienia z powodu wielu operacji

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

Oto wolniejsza i bardziej czytelna wersja:

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

Ten jest wolniejszy z powodu licznika, długich nazw var i niewłaściwego użycia 1 var.

Aby go użyć, po prostu wykonaj następujące czynności:

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

Edycja: (2013/12/16)

NIE używaj z Operą 12.16 lub starszą! zajmie to prawie 2,5 razy więcej niż regex!

W przypadku Chrome to rozwiązanie zajmie od 14 do 20 ms dla 1 000 000 znaków.

Rozwiązanie wyrażenia regularnego zajmuje 11-14 ms dla tej samej kwoty.

Korzystanie z funkcji (na zewnątrz String.prototype) zajmie około 10–13 ms.

Oto użyty kod:

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

Wynik wszystkich rozwiązań powinien wynosić 100 000!

Uwaga: jeśli chcesz to funkcja liczyć więcej niż 1 char, gdzie zmiany c=(c+'')[0]wc=c+''


1
prototyp był PRZYKŁADEM! Możesz użyć tej funkcji, jak chcesz! Możesz nawet to zrobić: var timesFunctionExist = funkcja (x, c) {var t = 0, l = 0, c = (c + '') [0]; while (l = x.indexOf (c, l) +1 ) ++ t; return t}); alert (timesCharExist („Znak” a „pojawia się tylko dwukrotnie”, „a”)) ;! (przyspieszy to trochę bardziej, bo nie będę bałaganu z prototypami). Jeśli uważasz, że się mylę, dlaczego nie pokazujesz tego, zanim rzucisz we mnie kamieniami? Udowodnij mi, że moja funkcja jest do bani, a ja ją zaakceptuję. Pokaż mi przypadek testowy. Długość dystansów ma wpływ na prędkość. Możesz to przetestować.
Ismael Miguel


4

Myślę, że cel wyrażenia regularnego jest bardzo różny od indexOf. indexOfpo prostu znajdź wystąpienie określonego ciągu, podczas gdy w wyrażeniu regularnym możesz używać symboli wieloznacznych, [A-Z]co oznacza, że ​​znajdzie on dowolny wielki znak w słowie bez podawania rzeczywistego znaku.

Przykład:

 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, ale musiałem zrobić coś takiego dzisiaj i pomyślałem tylko o sprawdzeniu SO później. Działa dla mnie dość szybko.

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

Patrz: - policz podciąg pojawi się w ciągu, aby uzyskać wyjaśnienie krok po kroku.


3

Opierając się na odpowiedzi @ Vittim.us powyżej. Podoba mi się kontrola, jaką zapewnia mi jego metoda, dzięki czemu łatwo ją rozszerzać, ale musiałem dodać niewrażliwość na wielkość liter i ograniczać dopasowania do całych słów z obsługą interpunkcji. (np. „kąpiel” oznacza „kąpiel”. ale nie „kąpiel”)

Wyrażenie interpunkcyjne pochodzi z: https://stackoverflow.com/a/25575009/497745 ( Jak mogę usunąć całą interpunkcję z ciągu znaków w JavaScript za pomocą 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;
}

Jeśli zauważysz błędy lub ulepszenia, możesz modyfikować i refaktoryzować tę odpowiedź.


3

Dla każdego, kto znajdzie ten wątek w przyszłości, zauważ, że zaakceptowana odpowiedź nie zawsze zwróci poprawną wartość, jeśli ją uogólnisz, ponieważ dusi operatorów wyrażeń regularnych, takich jak $i .. Oto lepsza wersja, która poradzi sobie z każdą igłą:

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

Użyj get_occurrence (varS, string), aby znaleźć występowanie zarówno znaków, jak i łańcucha w ciągu.


2

Spróbuj

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

Nikt tego nigdy nie zobaczy, ale dobrze jest raz na jakiś czas przywracać funkcje rekurencyjne i strzałkowe (celowo wspaniale)

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


1

Teraz jest to bardzo stary wątek, z którym się zetknąłem, ale ponieważ wielu opublikowało swoje odpowiedzi, oto mój, mając nadzieję, że pomogę komuś z tym prostym kodem.

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

Nie jestem pewien, czy jest to najszybsze rozwiązanie, ale wolałem to ze względu na prostotę i nie używanie wyrażenia regularnego (po prostu nie lubię ich używać!)


1

Ta funkcja zwraca liczbę wystąpień słowa w tekście.

Zauważ, że używamy toLowerCase do obliczania liczby wystąpień niezależnie od formatu (wielka, wielka ...) słowa i tekstu

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

0

Odpowiedź dla Leandro Batisty: tylko problem z wyrażeniem regularnym.

 "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

Trochę późno, ale zakładając, że mamy następujący ciąg:

var temp = "This is a string.";

Najpierw podzielimy się na wszystko, co chcesz dopasować, to zwróci tablicę ciągów.

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

Następnie otrzymujemy jego długość i odejmujemy od niej 1, ponieważ domyślnie split ma tablicę o rozmiarze 1 i konsekwentnie zwiększa jej rozmiar za każdym razem, gdy znajdzie zdarzenie.

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

Możesz to zrobić w jednym wierszu w następujący sposób:

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

Mam nadzieję, że to pomaga: D


1
Czy mogę oflagować to jako duplikat odpowiedzi? Może powinieneś przeczytać wszystkie odpowiedzi przed podaniem własnych?
Michiel

2
To @Orbit „s odpowiedź osiem lat później ...
aloisdg przeprowadzce do codidact.com

1
Czy powinienem zatem usunąć tę odpowiedź?
Juan Enrique Segebre

0

To rozwiązanie opiera się na .replace()metodzie, która akceptuje RegEx jako pierwszy parametr i funkcję jako drugi parametr , którego możemy użyć jako zamknięcia do zwiększenia licznika ...

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

Stosowanie

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

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

0

natknąłem się na ten 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
}

Ten sam algorytm można określić krócej:

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 przetłumaczone na JavaScript z 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
}

Sprawdź funkcję translatora php w Locutus substr_count


-2

Spróbuj tego:

function countString(str, search){
    var count=0;
    var index=str.indexOf(search);
    while(index!=-1){
        count++;
        index=str.indexOf(search,index+1);
    }
    return count;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.