Как посчитать вхождение строки в строку?

354

Как я могу подсчитать количество раз, когда конкретная строка встречается в другой строке. Например, это то, что я пытаюсь сделать в Javascript:

var temp = "This is a string.";
alert(temp.count("is")); //should output '2'
  • 15
    Это зависит от того, принимаете ли вы перекрывающиеся экземпляры, например, var t = "sss"; Сколько экземпляров подстроки "ss" находится в строке выше? 1 или 2? Вы перепрыгиваете через каждый экземпляр или перемещаете указатель посимвольно, ища подстроку?
  • 0
    Улучшенный тест для ответов на этот вопрос: jsperf.com/string-ocurrence-split-vs-match/2 (основан на тесте Kazzkiq).
Теги:
string

22 ответа

683
Лучший ответ

g в регулярном выражении (short for global) говорит, чтобы искать всю строку, а не просто найти первое вхождение:

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

Это соответствует is дважды. И если совпадений нет, он возвращает 0.

var temp1 = "Hello World!";
var count1 = (temp1.match(/is/g) || []).length;
console.log(count1);
  • 1
    Просто убедитесь , что вы процитировать слово для поиска ( is , в данном случае) , если оно содержит какие - либо специальные символы.
  • 3
    современный и элегантный, но решение Vitimtk гораздо эффективнее. что вы все думаете о его коде?
Показать ещё 15 комментариев
183
/** 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;
}

Использование

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

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

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

allowOverlapping

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

Матчи:

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

Unit Test

Benchmark

Я сделал тестовый тест, и моя функция более 10 раз быстрее, чем функция соответствия регулярному выражению, отправленная gumbo. В моем тесте строка - 25 символов. с 2 вхождениями символа 'o'. я выполненный 1 000 000 раз в Сафари.

Safari 5.1

Benchmark > Общее время выполнения: 5617 мс (регулярное выражение)

Benchmark > Общее время исполнения: 881 мс (моя функция в 6.4 раза быстрее)

Firefox 4

Benchmark > Общее время исполнения: 8547 мс (Rexexp)

Benchmark > Общее время исполнения: 634 мс (моя функция на 13,5 раз быстрее)


Изменить: изменения, которые я сделал

  • длина кешированной строки

  • добавлен тип-литье в строку.

  • добавлен необязательный параметр "allowOverlapping"

  • исправленный корректный вывод для "пустого случая подстроки".

Суть
  • 11
    +1 за возвращение циклов while! (и быть быстрым)
  • 3
    Я повторил этот тест в Safari 5 и получил аналогичные результаты с небольшой (100b) строкой, но с большей строкой (16kb) для меня регулярное выражение выполнялось быстрее. Для одной итерации (не 1 000 000) разница в любом случае была меньше миллисекунды, поэтому мой голос переходит к регулярному выражению.
Показать ещё 17 комментариев
53

Вы можете попробовать следующее:

var theString = "This is a string.";
console.log(theString.split("is").length - 1);
  • 6
    Мой любимый!
  • 8
    +1 для простоты и потому что, согласно моим тестам, это решение работает в ~ 10 раз быстрее, чем другие!
Показать ещё 4 комментария
52
function countInstances(string, word) {
   var substrings = string.split(word);
   return substrings.length - 1;
}
  • 4
    Это небезопасный / неточный подход, например: countInstances("isisisisisis", "is") === 0 .
  • 12
    возвращает 6 для меня ...
Показать ещё 7 комментариев
24

Мое решение:

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'));
  • 3
    рад, что прокрутил вниз для этого!
  • 1
    я тоже! Простое элегантное решение. Работает отлично.
Показать ещё 2 комментария
16

Вы можете использовать match для определения такой функции:

String.prototype.count = function(search) {
    var m = this.match(new RegExp(search.toString().replace(/(?=[.\\+*?[^\]$(){}\|])/g, "\\"), "g"));
    return m ? m.length:0;
}
  • 1
    Если бы вы хотели, чтобы оно было единообразно с семантикой поиска в JS, возвращаемая строка возвращала бы return m ? m.length:-1; ,
  • 0
    Это лучше, чем другие решения regex, приведенные выше, потому что они вызывают ошибку, если строка для подсчета вхождений равна «[» или что-либо со специальным значением в Regex.
7

Вот самая быстрая функция!

Почему это быстрее?

  • Не проверяет char на char (с одним исключением)
  • Использует некоторое время и увеличивает 1 var (char count var) против цикла for, проверяя длину и увеличивая 2 vars (обычно var я и var с подсчетом char)
  • Использование WAY less vars
  • Не использовать регулярное выражение!
  • Использует (надеюсь) высоко оптимизированную функцию
  • Все операции объединены так, как они могут быть, избегая замедление из-за нескольких операций

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

Ниже представлена ​​более медленная и читаемая версия:

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

Это медленнее из-за счетчика, длинных имен переменных и неправильного использования 1 var.

Чтобы использовать его, вы просто делаете это:

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

Изменить: (2013/12/16)

НЕ использовать с Opera 12.16 и старше! это займет почти в 2,5 раза больше, чем регулярное выражение!

На хром это решение займет от 14 мс до 20 мс для 1 000 000 символов.

Регулярное решение занимает 11-14 мс для той же суммы.

Использование функции (вне String.prototype) займет около 10-13 мс.

Вот код:

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

Результат всех решений должен быть 100 000!

Примечание. Если вы хотите, чтобы эта функция считала более 1 char, измените, где c=(c+'')[0] на c=c+''

  • 1
    прототип был ПРИМЕР! Вы можете использовать функцию, как вам угодно! Вы даже можете сделать это: var timesFunctionExist = function (x, c) {var t = 0, l = 0, c = (c + '') [0]; while (l = x.indexOf (c, l) +1 ) ++ t; вернуть t}); alert (timesCharExist ('Символ "a" появляется только дважды "," a ")) ;! (это ускорит немного больше, потому что я не буду возиться с прототипами). Если вы думаете, что я неправ, почему бы вам не показать это, прежде чем бросать в меня камни? Докажите мне, что моя функция отстой, и я приму ее. Покажите мне контрольный пример. И длина варов действительно влияет на скорость. Вы можете проверить это.
  • 0
    Извините за долгую задержку. Проверьте редактирование.
Показать ещё 1 комментарий
7

Просто играйте в гольф с вышеупомянутым решением: -)

alert("This is a string." + match(/is/g).length);

  • 9
    Это должно быть alert(("This is a string.".match(/is/g) || []).length); в противном случае вы получите неопределенную ошибку, если совпадений не найдено.
6

Версия без регулярного выражения:

 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
    Работало отлично! С регулярным выражением сложнее, на самом деле это проще.
  • 0
    1. Это только для поиска по одному символу, слишком тонкий 2. даже OP запрашивает is
5

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

String.prototype.Count = function (find) { return this.split(find).length - 1; } "This is a string.".Count("is");

Это вернет 2.

  • 0
    Это ответ @Orbit шесть лет спустя ...
3

Я думаю, что цель для регулярного выражения сильно отличается от indexOf. indexOf просто найдите видимость определенной строки, в то время как в регулярном выражении вы можете использовать подстановочные знаки, такие как [A-Z], что означает, что он найдет символ любой в слове без указания фактического символа.

Пример:

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

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

Используйте get_occurrence (varS, string), чтобы найти вхождения обоих символов и строки в String.

2

Основываясь на ответе @Vittim.us выше. Мне нравится контроль, который дает мне его метод, что упрощает его расширение, но мне нужно было добавить нечувствительность к регистру и ограничить совпадение целыми словами с поддержкой пунктуации. (например, "ванна" находится в "ванне", но не "купается" ).

Регулярное выражение для пунктуации произошло от: https://stackoverflow.com/questions/4328500/how-can-i-strip-all-punctuation-from-a-string-in-javascript-using-regex (Как я могу удалить все знаки препинания из строки в JavaScript с помощью 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;
}

Пожалуйста, не стесняйтесь изменять и реорганизовывать этот ответ, если вы обнаружите ошибки или улучшения.

2

Для тех, кто находит этот поток в будущем, обратите внимание, что принятый ответ не всегда вернет правильное значение, если вы его обобщите, так как оно будет задыхаться от операторов регулярных выражений, таких как $ и .. Здесь лучшая версия, которая может обрабатывать любую иглу:

function occurrences (haystack, needle) {
  var _needle = needle
    .replace(/\[/g, '\\[')
    .replace(/\]/g, '\\]')
  return (
    haystack.match(new RegExp('[' + _needle + ']', 'g')) || []
  ).length
}
2
       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");

Обратитесь: подсчитать подстроку в строке для пошагового объяснения.

2

Супер пупер старый, но мне нужно было сделать что-то подобное сегодня и только подумать, чтобы потом проверить SO. Работает довольно быстро для меня.

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

Простая версия без регулярного выражения:

var temp = "This is a string.";

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

alert(count);
  • 0
    Это ответ @Orbit шесть лет спустя ...
1

Попробуйте

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

Ответа на этот вопрос Leandro Batista: просто проблема с выражением регулярного выражения.

 "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 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[letter.length - 1];
var count;
for (var i = count = 0; i < search_value.length; count += (search_value[i++] == letter));
console.log(count);

Я не уверен, что это самое быстрое решение, но я предпочел его для простоты и не использовал регулярное выражение (мне просто не нравится использовать их!)

-2

Попробуйте следующее:

function countString(str, search){
    var count=0;
    var index=str.indexOf(search);
    while(index!=-1){
        count++;
        index=str.indexOf(search,index+1);
    }
    return count;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню