Dizedeki dize oluşumunu nasıl sayabilirim?


608

Belirli bir dizenin başka bir dizede gerçekleşme sayısını nasıl sayabilirim. Örneğin, bu Javascript yapmaya çalışıyorum:

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

19
Çakışan örnekleri kabul edip etmediğinize bağlıdır , örneğin var t = "sss"; Yukarıdaki dizede "ss" alt dizesinin kaç örneği var? 1 yada 2? Her örnek üzerinde bir sıçrayış yapıyor musunuz veya alt dizeyi aramak için işaretçiyi karakter karakter hareket ettiriyor musunuz?
Tim

4
Bu sorunun cevapları için geliştirilmiş bir karşılaştırma ölçütü: jsperf.com/string-ocurrence-split-vs-match/2 (Kazzkiq'in karşılaştırmalı değerlendirmesine dayanarak).
idmean

Yanıtlar:


1028

g(Kısaltması normal ifadede küresel ) sadece ilk geçtiği bulmak yerine tüm dizeyi aramak için söylüyor. Bu isiki kez eşleşir :

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

Eşleşme yoksa döndürür 0:

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


3
modern ve zarif, ancak Vitimtk'in çözümü çok daha verimli. Hepiniz onun kodu hakkında ne düşünüyorsunuz?
TruMan1

5
Bu soruya en iyi yanıtı verir. Birisi "Özel durumda 10x daha hızlı nasıl yapabilirim (regexps olmadan)" diye sordu Vitimtk bu soruyu kazanacaktı.
Dzhaughn

121
Bunun için teşekkürler .. count = (str.match(/is/g) || []).lengthEğer bir maçın yoksa ele almak için gittim .
Matt

6
Bu cevabın soruyla tam olarak eşleştiğini düşünmüyorum, çünkü kullanım senaryosunun açıkladığı gibi bir dize eşleşmesi için argüman almıyor. Elbette, yapıcıyı kullanarak RegExpve aradığınız dizeyi geçirerek dinamik olarak regexp oluşturabilirsiniz , ancak bu durumda tüm metakarakterlerden kaçmanız gerekir. Bu senaryoda, saf bir dize yaklaşımı tercih edilir.
ZER0

3
Matt'in cevabı cevapta olmalı!
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;
}

kullanım

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

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

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

allowOverlapping

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

Maçlar:

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

Ünite testi

Karşılaştırma

Bir kıyaslama testi yaptım ve fonksiyonum gumbo tarafından yayınlanan regexp match fonksiyonundan 10 kat daha hızlı. Test dizimde 25 karakter uzunluğunda. 'o' karakterinin 2 oluşumuyla. Safari'de 1000 000 kez idam ettim.

Safari 5.1

Deney> Toplam süre yürütme: 5617 ms (normal ifade)

Deney> Toplam süre yürütme: 881 ms (işlevim 6,4 kat daha hızlı)

Firefox 4

Deney> Toplam yürütme süresi: 8547 ms (Rexexp)

Deney> Toplam süre yürütme: 634 ms (işlevim 13,5x daha hızlı)


Düzenleme: yaptığım değişiklikler

  • önbelleklenmiş alt dize uzunluğu

  • dizeye tip döküm eklendi.

  • isteğe bağlı 'allowOverlapping' parametresi eklendi

  • "" boş alt dize kasası için doğru çıkış düzeltildi.

öz

5
Bu testi Safari 5'te tekrarladım ve küçük (100b) bir dize ile benzer sonuçlar aldım, ancak daha büyük bir dize (16kb) ile normal ifade benim için daha hızlı koştu. Bir yineleme için (1.000.000 değil), fark yine de milisaniyeden azdı, bu yüzden oyum normal ifadeye gidiyor.
arlomedia

2
+1, ancak substring.lengthhemen hemen her döngüyü kontrol ediyorsunuz, dışında önbelleğe almayı düşünmelisinizwhile
ajax333221

1
@ ajax333221 OMG fikrimi okudun, bu iyileştirmeyi birkaç gün önce yaptım ve cevabımı düzenleyecektim jsperf.com/count-string-occurrence-in-string
Vitim.us

4
Kodunuzu burada kullanımda buldum: success-equation.com/mind_reader.html . Gerçekten güzel programcı orada bir referans koyarak fikirli.
Bruno Kim

3
@DanielZuzevich eğer sen- ebilmek yapmak türleri ve String çalışır zorlar ve occurrences(11,1) //2hala çalışır. (Türleri kontrol etmek ve toString () öğesini çağırmak yerine bu yolu daha hızlı yapıyor )
Vitim.us

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

4
Bu, örneğin bir güvensiz / yanlış yaklaşım şöyledir: countInstances("isisisisisis", "is") === 0.
Nick Craver

5
@Antal - Chrome'un önceki beta derlemesinde bir hata gibi görünüyor, en son sürüme güncellendikten sonra çalışıyor, yine de bu yöntemin açık olmasına dikkat ediyorum.
Nick Craver

28
Bu benim için tamamen geçerli bir çözüm gibi görünüyor.
Gregor Schmidt

2
@NickCraver meraktan, neden bu yöntemden uzak durmak istiyorsun? (beta tarayıcınızdaki hata dışında)
Jonny Lin

6
@JonnyLin alternatifler yapmadığında hemen atmak gereksiz tahsisler oluşturur - verilere bağlı olarak potansiyel olarak çok büyük olanlar.
Nick Craver

88

Bunu deneyebilirsiniz:

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


14
Basitlik için +1 ve testlerime göre bu çözüm diğerlerinden ~ 10 kat daha hızlı çalışıyor !
Claudio Holanda

Örneğin, iki "is" var, her birinin pozisyonunu nasıl elde edersiniz?
Rapidoodle

@Orbit'in cevabında tartışıldığı gibi, insanlar Chrome'un eski sürümlerinde farklı sonuçlar alıyor. Bu yöntemi kullanırken belki biraz temkinli olurdum.
16:15

Ve değişkenlerle de kullanabilirsiniz: theString.split(myvar).length - 1basit regex ile yapamayacağınız
Steffan

4
Bu @Orbit'in cevabı üç yıl sonra ...
aloisdg, codidact.com'a taşınıyor

33

Çözümüm:

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
belki (str.match (regExp) || []) uzunluğu döndürmek daha iyi olur; Bu şekilde normal ifadeyi iki kez değerlendirmez misiniz?
aikeru

2
aynı zamanda countOcurrences('Hello...','.')==8
ipinizi çekip almanız gerekir

19

Bu matchişlevi tanımlamak için kullanabilirsiniz :

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

1
JS'nin arama semantiği ile aynı olmasını isteseydiniz, dönüş satırı olurdu return m ? m.length:-1;.
Conor O'Brien

Bu, yukarıdaki diğer normal ifade çözümlerinden daha iyidir, çünkü dizenin "[" sayısını sayacaksa veya Regex'te özel bir anlamı olan bir hataya neden olurlar.
programmer5000

11

Normal olmayan sürüm:

 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. Sadece tek karakter arama için var, çok ince 2. hatta OP sorar isbulunuşu
vladkras

1
Bu muhtemelen en hızlı uygulama, ancak "++ pos" yerine "pos + =
searchFor.length



8

İşte en hızlı fonksiyon!

Neden daha hızlı?

  • Karakteri char ile kontrol etmez (1 istisna hariç)
  • Bir süre kullanır ve uzunluğu kontrol etmek için 2 var'lık bir artış kullanır ve 1 var (karakter sayısı var) ve a değerlerini artırır (genellikle char sayısı ile var i ve bir var)
  • YOL daha az değişken kullanır
  • Normal ifade kullanmaz!
  • (Umarım) yüksek derecede optimize edilmiş bir fonksiyon kullanır
  • Tüm işlemler olabildiğince birleştirilir ve birden fazla işlemden kaynaklanan yavaşlamalardan kaçınır

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

İşte daha yavaş ve daha okunabilir bir sürüm:

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

Sayaç, uzun var isimleri ve 1 var kötüye kullanımı nedeniyle bu daha yavaştır.

Kullanmak için bunu yapmanız yeterlidir:

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

Düzenleme: (2013/12/16)

Opera 12.16 veya daha eski sürümlerle KULLANMAYIN! regex çözümünden yaklaşık 2,5 kat daha fazla sürecek!

Chrome'da bu çözüm, 1.000.000 karakter için 14ms ile 20ms arasında sürecektir.

Regex çözeltisi aynı miktarda 11-14 ms sürer.

Bir fonksiyonun (dışarıda String.prototype) kullanılması yaklaşık 10-13 ms sürecektir.

Kullanılan 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');

Tüm çözümlerin sonucu 100.000 olmalı!

Not: Bu fonksiyon 1'den fazla kömürü saymak değişiklik istiyorsanız nerede c=(c+'')[0]içinec=c+''


1
prototip bir örnekti! Fonksiyonu istediğiniz gibi kullanabilirsiniz! Bunu bile yapabilirsiniz: var timesFunctionExist = function (x, c) {var t = 0, l = 0, c = (c + '') [0]; (l = x.indexOf (c, l) +1 ) ++ t; dönüş t}); alert (timesCharExist ('Karakter "a" yalnızca iki kez görünür "," a ")) ;! (Bu biraz daha hızlandıracaktır çünkü prototiplerle uğraşmayacağım). Yanlış olduğumu düşünüyorsan neden bana taş atmadan önce göstermiyorsun? Kanıtlamak benim işlev berbat ve ben kabul edeceğim. Bana bir test vakası göster. Ve değişkenlerin uzunluğunun hız üzerinde etkisi vardır. Test edebilirsiniz.
Ismael Miguel

7

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


4

Bence normal ifadenin amacı bundan çok farklı indexOf. indexOfregex'te joker karakterleri kullanabilirken , gerçek karakteri belirtmeden kelimedeki herhangi bir büyük karakteri [A-Z]bulacağı anlamına gelir .

Misal:

 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, ama bugün böyle bir şey yapmam gerekiyordu ve sadece daha sonra SO'yu kontrol etmeyi düşündüm. Benim için oldukça hızlı çalışıyor.

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

Bakınız: - adım adım açıklama için dizede bir alt dize sayılır .


3

Yukarıdaki @ Vittim.us cevabı üzerine inşa. Metodunun bana verdiği kontrolü uzatmayı kolaylaştırarak seviyorum, ancak noktalama desteği ile büyük / küçük harf duyarsızlığı ve eşleşmeleri tüm kelimelere sınırlamak zorunda kaldım. (ör. "banyo" "banyo yap" dır, ancak "banyo" değildir)

Noktalama işaretli ifadesi şuradan geldi: https://stackoverflow.com/a/25575009/497745 ( Normal ifadeyi kullanarak JavaScript'teki bir dizeden tüm noktalama işaretlerini nasıl kaldırabilirim? )

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

Hataları veya iyileştirmeleri tespit ederseniz lütfen bu cevabı değiştirmekten ve yeniden düzenlemekten çekinmeyin.


3

Gelecekte bu iş parçacığını bulan herkes için, kabul ettiğiniz yanıtın genelleştirmeniz durumunda her zaman doğru değeri döndürmeyeceğini unutmayın, çünkü $ve gibi regex operatörlerini boğacaktır .. İşte herhangi bir iğneyi işleyebilecek daha iyi bir sürüm :

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

Bir String'de hem karakterlerin hem de dizenin oluşumunu bulmak için get_occurrence (varS, string) kullanın.


2

Dene

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

Hiç kimse bunu görmeyecek, ancak arada sırada tekrarlama ve ok işlevlerini geri getirmek iyi (pun şanlı bir şekilde tasarlanmış)

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


1

Şimdi bu karşılaştığım çok eski bir iş parçacığı ama birçok kişinin cevaplarını ittiği için, bu basit kodla birine yardım etme umuduyla benim.

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

En hızlı çözüm olup olmadığından emin değilim ama basitlik ve regex kullanmama için tercih ettim (sadece onları kullanmaktan hoşlanmıyorum!)


1

Bu işlev, metindeki bir sözcüğün tekrarlama sayısını döndürür.

Kelimenin ve metnin biçimi (büyük harf, büyük harf ...) ne olursa olsun yineleme sayısını hesaplamak için toLowerCase kullandığımızı unutmayın.

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

0

Leandro Batista için cevap: sadece normal ifade ile ilgili bir sorun.

 "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

Biraz geç ama aşağıdaki dizeye sahip olduğumuzu varsayarsak:

var temp = "This is a string.";

Öncelikle eşleştirmek istediğiniz her şeye ayrılırız, bu bir dizi dizeyi döndürür.

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

Sonra uzunluğu alır ve 1'i çıkarırız, çünkü bölünmüş varsayılan olarak 1 büyüklüğünde bir diziye ayarlanır ve sonuç olarak her seferinde bir bulduğu zaman boyutunu artırır.

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

Tüm bunları aşağıdaki gibi tek bir satırda da yapabilirsiniz:

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

Umarım yardımcı olur: D


1
Bunu mükerrer cevap olarak işaretleyebilir miyim? Belki kendi cevaplarınızı vermeden önce tüm cevapları okumalısınız?
Michiel

2
Bu @Orbit'in cevabı sekiz yıl sonra ...
aloisdg, codidact.com'a taşınıyor

1
O zaman bu yanıtı silmeli miyim?
Juan Enrique Segebre

0

Bu çözüm, .replace()bir RegEx'i ilk parametre olarak kabul eden yönteme ve ikinci parametreyi bir sayacı artırmak için bir kapatma olarak kullanabileceğimiz bir işleve dayanır ...

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

kullanım

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

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

0

bu yayına rastladı.

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
}

Aynı algoritma daha kısa düzenlenebilir:

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 php sitesinden Javascript'e çevrildi


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
}

Locutus'un Php'nin substr_count işlevine çevirisine göz atın


-2

Bunu dene:

function countString(str, search){
    var count=0;
    var index=str.indexOf(search);
    while(index!=-1){
        count++;
        index=str.indexOf(search,index+1);
    }
    return count;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.