Как бы вы посчитали вхождения строки (на самом деле символ) внутри строки?

865

Я делаю что-то, где я понял, что хочу посчитать, сколько /я могу найти в строке, и потом меня поразило, что было несколько способов сделать это, но я не мог определиться с тем, что лучше (или проще) было ,

На данный момент я собираюсь с чем-то вроде:

string source = "/once/upon/a/time/";
int count = source.Length - source.Replace("/", "").Length;

Но мне это совсем не нравится, любители?

Я действительно не хочу выкапывать RegExэто, не так ли?

Я знаю, что в моей строке будет искомый термин, поэтому вы можете предположить, что ...

Конечно , для строк , где длина> 1 ,

string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;
несмотря
источник
34
+1: я должен сказать, что это совсем другой способ считать. я удивлен результатами тестов :)
naveen
4
Это не так уж отличается ... это типичный способ реализации этой функции в SQL: LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N","")).
Шеридан
6
На самом деле вы должны разделить на "/".Length
Gerard
3
Могу я спросить, что, по вашим требованиям, должно быть подсчитано для количества вхождений "//" в "/////"? 2 или 4?
Les
1
Использование регулярных выражений, вероятно, лучший способ сделать это
Адам Хиггинс

Ответы:

1010

Если вы используете .NET 3.5, вы можете сделать это в одну строку с LINQ:

int count = source.Count(f => f == '/');

Если вы не хотите использовать LINQ, вы можете сделать это с:

int count = source.Split('/').Length - 1;

Вы можете быть удивлены, узнав, что ваша оригинальная техника кажется примерно на 30% быстрее, чем любой из них! Я только что сделал быстрый тест с "/ один раз / на / время /", и результаты следующие:

Ваш исходный источник =
12s.Count =
источник 19s.Split = 17s
foreach ( из ответа Бобвинхольта ) = 10s

(Время для 50 000 000 итераций, поэтому вы вряд ли заметите большую разницу в реальном мире.)

LukeH
источник
6
Да, VS скрывает методы расширения LINQ для строкового класса. Я думаю, они решили, что разработчики не захотят, чтобы все эти методы расширения отображались в строковом классе. Вероятно, мудрое решение.
Иуда Габриэль Химанго
11
Возможно, это связано с тем, что VS2010 автоматически включает System.Linq в новые файлы классов, а VS2008, вероятно, этого не делает. Пространство имен должно быть в наличии для работы intellisense.
Sprague
30
Обратите внимание, что решения Count и Split будут работать только при подсчете символов. Они не будут работать со строками, как решение OP.
Питер Лиллевольд
5
f == '\' о символах в строке, а не в строке
Томас Веллер
9
Это похоже на ответ на другой вопрос: «Как бы вы посчитали появления символа в строке?»
Бен Ааронсон
181
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

Должно быть быстрее, чем само source.Replace()по себе.

bobwienholt
источник
18
Вы можете получить незначительное улучшение, переключившись на for вместо foreach, но только чуть-чуть.
Отметить
17
Нет. Вопрос состоит в том, чтобы посчитать вхождение строки, а не символа.
ЮкиСакура
3
Это подсчет символов в строке. Название о подсчете строк в строке
Томас Уэллер
2
@Mark Только что проверил это с помощью цикла for, и это было на самом деле медленнее, чем при использовании foreach. Может быть из-за проверки границ? (Время составило 1,65 с против 2,05 на 5-миллионных итерациях.)
Измерение
4
Хотя вопрос состоит в том, чтобы задать строку внутри строки, пример задачи, опубликованной OP, на самом деле является всего лишь одним символом, и в этом случае я бы назвал этот ответ все еще допустимым решением, поскольку он показывает лучший способ (поиск по символу вместо поиска по строке) решить проблему под рукой.
Чад
136
int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;
Еще один создатель кода
источник
8
+1 - в некоторых случаях вы можете добавить RegexOptions.IgnoreCase.
TrueWill
3
разве это не невероятно низко?
Томас Аюб
4
Накладные расходы в Regex не идеальны, плюс "я действительно не хочу копать RegEx для этого, не так ли?"
Чад
возможно , не хотят Regex.Escape(...)такnew System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
barlop
2
Я пошел с этим, потому что он может искать строки, а не только символы.
Джеймс в Инди
86

Если вы хотите иметь возможность искать целые строки, а не только символы:

src.Select((c, i) => src.Substring(i))
    .Count(sub => sub.StartsWith(target))

Читайте как «для каждого символа в строке, оставшуюся часть строки, начинающуюся с этого символа, в качестве подстроки; считайте, если она начинается с целевой строки».

MQP
источник
1
Не уверен, как я могу объяснить это более понятным способом, чем приведенное описание. Что смущает?
MQP
58
СУПЕР МЕДЛЕННО! Попробовал его на странице HTML, и это заняло около 2 минут, в отличие от других методов на этой странице, которые заняли 2 секунды. Ответ был правильным; он был слишком медленным, чтобы его можно было использовать.
JohnB
2
согласился, слишком медленно. Я большой поклонник решений в стиле linq, но это просто нежизнеспособно.
Sprague
5
Обратите внимание, что причина, по которой это происходит так медленно, в том, что он создает n строк, таким образом выделяя примерно n ^ 2/2 байта.
Питер Крэбтри
6
OutOfMemoryException выбрасывается для моих 210000 символов строки.
окончание
66

Я провел некоторое исследование и обнаружил, что решение Ричарда Уотсона является самым быстрым в большинстве случаев. Это таблица с результатами каждого решения в посте (кроме тех, которые используют Regex, потому что он генерирует исключения при разборе строки типа "test {test")

    Name      | Short/char |  Long/char | Short/short| Long/short |  Long/long |
    Inspite   |         134|        1853|          95|        1146|         671|
    LukeH_1   |         346|        4490|         N/A|         N/A|         N/A|
    LukeH_2   |         152|        1569|         197|        2425|        2171|
Bobwienholt   |         230|        3269|         N/A|         N/A|         N/A|
Richard Watson|          33|         298|         146|         737|         543|
StefanosKargas|         N/A|         N/A|         681|       11884|       12486|

Вы можете видеть, что в случае нахождения количества вхождений коротких подстрок (1-5 символов) в короткую строку (10-50 символов) предпочтителен оригинальный алгоритм.

Также для подстроки с несколькими символами следует использовать следующий код (на основе решения Ричарда Уотсона )

int count = 0, n = 0;

if(substring != "")
{
    while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
    {
        n += substring.Length;
        ++count;
    }
}

Я собирался добавить свое собственное «низкоуровневое» решение (без создания подстрок, использования replace / split или каких-либо Regex / Linq), но ваше, возможно, даже лучше, чем мое (и, по крайней мере, короче). Спасибо!
Дэн Вт

Для решений Regex, добавьтеRegex.Escape(needle)
Тимин

2
Просто чтобы указать на других, нужно искать значение поиска, если оно пустое, иначе вы попадете в бесконечный цикл.
WhoIsRich

2
Может быть, это только я, но source="aaa" substring="aa"я ожидал получить обратно 2, а не 1. Чтобы «исправить» это, измените n += substring.Lengthнаn++
ytoledano

Вы можете добавить overlappedфлаг, чтобы удовлетворить ваш случай, как это:overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
tsionyx

54

LINQ работает со всеми коллекциями, и так как строки - это просто коллекция символов, как насчет этой милой строчки:

var count = source.Count(c => c == '/');

Убедитесь, что у вас есть using System.Linq;верхняя часть файла кода, как .Countи метод расширения из этого пространства имен.


5
Стоит ли там использовать var? Есть ли шанс, что Count будет заменен чем-то, что не возвращает int?
Whatsit

70
@Whatsit: вы можете печатать «var» только левой рукой, а «int» - обеими руками;)
Шон Брайт

7
intвсе буквы находятся в домашних ключах, а varнет. эээ ... подожди, я использую Дворжак
Майкл Буэн

2
@BDotA Убедитесь, что у вас есть «using System.Linq;» в верхней части вашего файла. Кроме того, intellisense может скрыть от вас вызов .Count, поскольку это строка. Тем не менее, он будет компилироваться и работать просто отлично.
Иуда Габриэль Химанго

3
@JudahGabrielHimango Я бы сказал, что var следует использовать, особенно когда тип переменной очевиден (для краткости и согласованности)
EriF89

50
string source = "/once/upon/a/time/";
int count = 0;
int n = 0;

while ((n = source.IndexOf('/', n)) != -1)
{
   n++;
   count++;
}

На моем компьютере это примерно на 2 секунды быстрее, чем решение для каждого символа за 50 миллионов итераций.

Редакция 2013 года:

Измените строку на char [] и повторите это. Сокращает еще одну или две секунды от общего времени для итераций на 50 м!

char[] testchars = source.ToCharArray();
foreach (char c in testchars)
{
     if (c == '/')
         count++;
}

Это еще быстрее:

char[] testchars = source.ToCharArray();
int length = testchars.Length;
for (int n = 0; n < length; n++)
{
    if (testchars[n] == '/')
        count++;
}

Для правильной оценки, итерация от конца массива до 0 кажется самой быстрой, примерно на 5%.

int length = testchars.Length;
for (int n = length-1; n >= 0; n--)
{
    if (testchars[n] == '/')
        count++;
}

Я задавался вопросом, почему это могло быть и гуглил вокруг (я помню кое-что об обратной итерации, которая была быстрее), и натолкнулся на этот SO вопрос, который досадно использует технику строки для char [] уже. Я думаю, что уловка разворота является новой в этом контексте, хотя.

Какой самый быстрый способ перебора отдельных символов в строке в C #?


1
Вы можете поставить source.IndexOf('/', n + 1)и потерять n++скобки и время while :) Кроме того, string word = "/"вместо символа укажите переменную .
neeKo

1
Эй, Нико, зацени новые ответы. Хотя сделать подстроку переменной длины будет сложнее.
Ричард Уотсон

Я использовал нечто подобное, шагая по подстроке; пока я не понял, что indexOf имеет startIndex. Мне больше всего нравится первое решение, так как это хороший баланс между скоростью и объемом памяти.
Самир Банянович

1
Я где-то читал, что быстрее итерировать в обратном направлении, потому что быстрее сравнивать значение с 0
reggaeguitar

1
@shitpoet да. Если вы посмотрите на базовый код, это нативный вызов. public char [] toCharArray () {... System.arraycopy (значение, 0, результат, 0, значение.длина); ...}

источник
46

Оба они работают только для односимвольных поисковых терминов ...

countOccurences("the", "the answer is the answer");

int countOccurences(string needle, string haystack)
{
    return (haystack.Length - haystack.Replace(needle,"").Length) / needle.Length;
}

может оказаться лучше для более длинных игл ...

Но должен быть более элегантный способ. :)

ZombieSheep
источник
Для учета многосимвольных замен. Без этого подсчет «в» тесте является ключом »вернул бы 6.
ZombieSheep
Сравнительный анализ и сравнение со строкой. Сплит-способ - работает примерно в 1,5 раза быстрее. Престижность.
Алекс
20

Редактировать:

source.Split('/').Length-1
Брайан Рудольф
источник
2
Это то, что я делаю. И source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1для многосимвольных разделителей.
bzlm
4
Это будет выполнять как минимум n выделений строк в куче, плюс (возможно) несколько переопределений массива - и все это только для подсчета? Чрезвычайно неэффективен, плохо масштабируется и никогда не должен использоваться в каком-либо важном коде.
Зар Шардан
17

В C # хорошим счетчиком String SubString является этот неожиданно хитрый парень:

public static int CCount(String haystack, String needle)
{
    return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}
Дейв
источник
1
Хорошее решение - и работает для строки тоже (не только символ)!
ChriPf
Спасибо, слишком легко забыть некоторые тонкости обработки строк при обмене языками - как это делают большинство из нас в наши дни!
Дейв
1
-1 потому что: вы знаете разницу между Count () и Count или Length? Если кто-то использует Count () вместо Count или Length, я получаю срабатывание. Count () создает IEnumerator, затем проходит через все вхождения IEnumerable, тогда как Count или Length уже задают свойства объекта, которые уже содержат желаемое количество, без необходимости перебирать все элементы.
аэрозон
Хорошее место, и что странно, это то, что в моей библиотеке, откуда я взял функцию, я использую «Длина». Под редакцией!
Дейв
15
Regex.Matches(input,  Regex.Escape("stringToMatch")).Count
cederlof
источник
1
Это неверно, если входные данные содержат специальные символы регулярного выражения т.е. | Там должен быть Regex.Escape (вход)
Эсбен Сков Педерсен
1
На самом деле stringToMatchпотребности уходят, а не input.
Теодор Зулиас
Вы правы. Починил это.
cederlof
13
private int CountWords(string text, string word) {
    int count = (text.Length - text.Replace(word, "").Length) / word.Length;
    return count;
}

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

Для контекста: я искал слова типа «не удалось» и «успешно» в файле журнала.

Гр, Бен

Бен
источник
2
Только не передавайте пустую строку для переменной «слово» (ошибка деления на ноль).
Эндрю Дженс
12
string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
  count++;
preetham
источник
20
или Regex.Matches (s, "65"). Количество ^ _ ^
Мета
Работает не для каждой строки. Попробуйте поискать "++" в "abc ++ def ++ xyz"
marsh-wiggle
7

Для тех, кто хочет использовать готовый метод расширения String,

вот что я использую, основываясь на лучших из опубликованных ответов:

public static class StringExtension
{    
    /// <summary> Returns the number of occurences of a string within a string, optional comparison allows case and culture control. </summary>
    public static int Occurrences(this System.String input, string value, StringComparison stringComparisonType = StringComparison.Ordinal)
    {
        if (String.IsNullOrEmpty(value)) return 0;

        int count    = 0;
        int position = 0;

        while ((position = input.IndexOf(value, position, stringComparisonType)) != -1)
        {
            position += value.Length;
            count    += 1;
        }

        return count;
    }

    /// <summary> Returns the number of occurences of a single character within a string. </summary>
    public static int Occurrences(this System.String input, char value)
    {
        int count = 0;
        foreach (char c in input) if (c == value) count += 1;
        return count;
    }
}
WhoIsRich
источник
Не будет ли второй метод бумом, если переданная строка будет нулевой или пустой? Чисто с точки зрения стиля, что вы определяете как ввод System.String, а не просто строка?
Нодоид
7
public static int GetNumSubstringOccurrences(string text, string search)
{
    int num = 0;
    int pos = 0;

    if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(search))
    {
        while ((pos = text.IndexOf(search, pos)) > -1)
        {
            num ++;
            pos += search.Length;
        }
    }
    return num;
}
user460847
источник
5

Я думаю, что самый простой способ сделать это - использовать регулярные выражения. Таким образом, вы можете получить то же количество разделений, что и при использовании myVar.Split ('x'), но с несколькими символами.

string myVar = "do this to count the number of words in my wording so that I can word it up!";
int count = Regex.Split(myVar, "word").Length;
BEROC
источник
3
string search = "/string";
var occurrences = (regex.Match(search, @"\/")).Count;

Это будет учитываться каждый раз, когда программа находит "/ s" точно (с учетом регистра), и число вхождений этого будет сохранено в переменной "вхождения"

Адам Хиггинс
источник
3

Я чувствовал, что нам не хватало определенных видов подсчета подстрок, таких как небезопасные побайтовые сравнения. Я собрал оригинальный метод плаката и любые методы, которые могли придумать.

Это расширения строк, которые я сделал.

namespace Example
{
    using System;
    using System.Text;

    public static class StringExtensions
    {
        public static int CountSubstr(this string str, string substr)
        {
            return (str.Length - str.Replace(substr, "").Length) / substr.Length;
        }

        public static int CountSubstr(this string str, char substr)
        {
            return (str.Length - str.Replace(substr.ToString(), "").Length);
        }

        public static int CountSubstr2(this string str, string substr)
        {
            int substrlen = substr.Length;
            int lastIndex = str.IndexOf(substr, 0, StringComparison.Ordinal);
            int count = 0;
            while (lastIndex != -1)
            {
                ++count;
                lastIndex = str.IndexOf(substr, lastIndex + substrlen, StringComparison.Ordinal);
            }

            return count;
        }

        public static int CountSubstr2(this string str, char substr)
        {
            int lastIndex = str.IndexOf(substr, 0);
            int count = 0;
            while (lastIndex != -1)
            {
                ++count;
                lastIndex = str.IndexOf(substr, lastIndex + 1);
            }

            return count;
        }

        public static int CountChar(this string str, char substr)
        {
            int length = str.Length;
            int count = 0;
            for (int i = 0; i < length; ++i)
                if (str[i] == substr)
                    ++count;

            return count;
        }

        public static int CountChar2(this string str, char substr)
        {
            int count = 0;
            foreach (var c in str)
                if (c == substr)
                    ++count;

            return count;
        }

        public static unsafe int CountChar3(this string str, char substr)
        {
            int length = str.Length;
            int count = 0;
            fixed (char* chars = str)
            {
                for (int i = 0; i < length; ++i)
                    if (*(chars + i) == substr)
                        ++count;
            }

            return count;
        }

        public static unsafe int CountChar4(this string str, char substr)
        {
            int length = str.Length;
            int count = 0;
            fixed (char* chars = str)
            {
                for (int i = length - 1; i >= 0; --i)
                    if (*(chars + i) == substr)
                        ++count;
            }

            return count;
        }

        public static unsafe int CountSubstr3(this string str, string substr)
        {
            int length = str.Length;
            int substrlen = substr.Length;
            int count = 0;
            fixed (char* strc = str)
            {
                fixed (char* substrc = substr)
                {
                    int n = 0;

                    for (int i = 0; i < length; ++i)
                    {
                        if (*(strc + i) == *(substrc + n))
                        {
                            ++n;
                            if (n == substrlen)
                            {
                                ++count;
                                n = 0;
                            }
                        }
                        else
                            n = 0;
                    }
                }
            }

            return count;
        }

        public static int CountSubstr3(this string str, char substr)
        {
            return CountSubstr3(str, substr.ToString());
        }

        public static unsafe int CountSubstr4(this string str, string substr)
        {
            int length = str.Length;
            int substrLastIndex = substr.Length - 1;
            int count = 0;
            fixed (char* strc = str)
            {
                fixed (char* substrc = substr)
                {
                    int n = substrLastIndex;

                    for (int i = length - 1; i >= 0; --i)
                    {
                        if (*(strc + i) == *(substrc + n))
                        {
                            if (--n == -1)
                            {
                                ++count;
                                n = substrLastIndex;
                            }
                        }
                        else
                            n = substrLastIndex;
                    }
                }
            }

            return count;
        }

        public static int CountSubstr4(this string str, char substr)
        {
            return CountSubstr4(str, substr.ToString());
        }
    }
}

Вслед за тестовым кодом ...

static void Main()
{
    const char matchA = '_';
    const string matchB = "and";
    const string matchC = "muchlongerword";
    const string testStrA = "_and_d_e_banna_i_o___pfasd__and_d_e_banna_i_o___pfasd_";
    const string testStrB = "and sdf and ans andeians andano ip and and sdf and ans andeians andano ip and";
    const string testStrC =
        "muchlongerword amuchlongerworsdfmuchlongerwordsdf jmuchlongerworijv muchlongerword sdmuchlongerword dsmuchlongerword";
    const int testSize = 1000000;
    Console.WriteLine(testStrA.CountSubstr('_'));
    Console.WriteLine(testStrA.CountSubstr2('_'));
    Console.WriteLine(testStrA.CountSubstr3('_'));
    Console.WriteLine(testStrA.CountSubstr4('_'));
    Console.WriteLine(testStrA.CountChar('_'));
    Console.WriteLine(testStrA.CountChar2('_'));
    Console.WriteLine(testStrA.CountChar3('_'));
    Console.WriteLine(testStrA.CountChar4('_'));
    Console.WriteLine(testStrB.CountSubstr("and"));
    Console.WriteLine(testStrB.CountSubstr2("and"));
    Console.WriteLine(testStrB.CountSubstr3("and"));
    Console.WriteLine(testStrB.CountSubstr4("and"));
    Console.WriteLine(testStrC.CountSubstr("muchlongerword"));
    Console.WriteLine(testStrC.CountSubstr2("muchlongerword"));
    Console.WriteLine(testStrC.CountSubstr3("muchlongerword"));
    Console.WriteLine(testStrC.CountSubstr4("muchlongerword"));
    var timer = new Stopwatch();
    timer.Start();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr(matchA);
    timer.Stop();
    Console.WriteLine("CS1 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr(matchB);
    timer.Stop();
    Console.WriteLine("CS1 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr(matchC);
    timer.Stop();
    Console.WriteLine("CS1 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr2(matchA);
    timer.Stop();
    Console.WriteLine("CS2 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr2(matchB);
    timer.Stop();
    Console.WriteLine("CS2 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr2(matchC);
    timer.Stop();
    Console.WriteLine("CS2 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr3(matchA);
    timer.Stop();
    Console.WriteLine("CS3 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr3(matchB);
    timer.Stop();
    Console.WriteLine("CS3 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr3(matchC);
    timer.Stop();
    Console.WriteLine("CS3 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountSubstr4(matchA);
    timer.Stop();
    Console.WriteLine("CS4 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrB.CountSubstr4(matchB);
    timer.Stop();
    Console.WriteLine("CS4 and: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrC.CountSubstr4(matchC);
    timer.Stop();
    Console.WriteLine("CS4 mlw: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar(matchA);
    timer.Stop();
    Console.WriteLine("CC1 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar2(matchA);
    timer.Stop();
    Console.WriteLine("CC2 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar3(matchA);
    timer.Stop();
    Console.WriteLine("CC3 chr: " + timer.Elapsed.TotalMilliseconds + "ms");

    timer.Restart();
    for (int i = 0; i < testSize; ++i)
        testStrA.CountChar4(matchA);
    timer.Stop();
    Console.WriteLine("CC4 chr: " + timer.Elapsed.TotalMilliseconds + "ms");
}

Результаты: CSX соответствует CountSubstrX, а CCX соответствует CountCharX. «chr» ищет строку для «_», «и» ищет строку для «and», а «mlw» ищет строку для «muchlongerword»

CS1 chr: 824.123ms
CS1 and: 586.1893ms
CS1 mlw: 486.5414ms
CS2 chr: 127.8941ms
CS2 and: 806.3918ms
CS2 mlw: 497.318ms
CS3 chr: 201.8896ms
CS3 and: 124.0675ms
CS3 mlw: 212.8341ms
CS4 chr: 81.5183ms
CS4 and: 92.0615ms
CS4 mlw: 116.2197ms
CC1 chr: 66.4078ms
CC2 chr: 64.0161ms
CC3 chr: 65.9013ms
CC4 chr: 65.8206ms

И, наконец, у меня был файл с 3,6 миллионами символов. Это был "derp adfderdserp dfaerpderp deasderp", повторенный 100 000 раз. Я искал "derp" внутри файла с помощью описанных выше методов 100 раз эти результаты.

CS1Derp: 1501.3444ms
CS2Derp: 1585.797ms
CS3Derp: 376.0937ms
CS4Derp: 271.1663ms

Так что мой 4-й метод определенно является победителем, но на самом деле, если файл с 3,6 миллионами символов 100 раз потребовал всего 1586 мс как худший случай, то все это довольно незначительно.

Кстати, я также отсканировал символ 'd' в 3,6-миллионном символьном файле со 100-кратными методами CountSubstr и CountChar. Результаты...

CS1  d : 2606.9513ms
CS2  d : 339.7942ms
CS3  d : 960.281ms
CS4  d : 233.3442ms
CC1  d : 302.4122ms
CC2  d : 280.7719ms
CC3  d : 299.1125ms
CC4  d : 292.9365ms

Исходя из этого, оригинальный метод постеров очень плох для односимвольных игл в большом стоге сена.

Примечание. Все значения были обновлены до версии выпуска. Я случайно забыл использовать режим Release при первой публикации этого сообщения. Некоторые из моих заявлений были изменены.

Николас Р. Грант
источник
Спасибо за результаты производительности. Разница в скорости в 10 раз может стать причиной не рассматривать linq или другое аккуратно написанное решение, а использовать метод расширения.
Андреас
2

Универсальная функция для появления строк:

public int getNumberOfOccurencies(String inputString, String checkString)
{
    if (checkString.Length > inputString.Length || checkString.Equals("")) { return 0; }
    int lengthDifference = inputString.Length - checkString.Length;
    int occurencies = 0;
    for (int i = 0; i < lengthDifference; i++) {
        if (inputString.Substring(i, checkString.Length).Equals(checkString)) { occurencies++; i += checkString.Length - 1; } }
    return occurencies;
}
Стефанос Каргас
источник
2
Это создает ОГРОМНОЕ количество временных строк и делает сборщик мусора очень тяжелым.
EricLaw
2
string source = "/once/upon/a/time/";
int count = 0, n = 0;
while ((n = source.IndexOf('/', n) + 1) != 0) count++;

Вариация ответа Ричарда Уотсона: немного быстрее с повышением эффективности, чем чаще встречается символ в строке, тем меньше кода!

Хотя я должен сказать, что без тщательного тестирования каждого сценария, я увидел очень значительное улучшение скорости с помощью:

int count = 0;
for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;
user2011559
источник
2
            var conditionalStatement = conditionSetting.Value;

            //order of replace matters, remove == before =, incase of ===
            conditionalStatement = conditionalStatement.Replace("==", "~").Replace("!=", "~").Replace('=', '~').Replace('!', '~').Replace('>', '~').Replace('<', '~').Replace(">=", "~").Replace("<=", "~");

            var listOfValidConditions = new List<string>() { "!=", "==", ">", "<", ">=", "<=" };

            if (conditionalStatement.Count(x => x == '~') != 1)
            {
                result.InvalidFieldList.Add(new KeyFieldData(batch.DECurrentField, "The IsDoubleKeyCondition does not contain a supported conditional statement. Contact System Administrator."));
                result.Status = ValidatorStatus.Fail;
                return result;
            }

Нужно сделать что-то похожее на проверку условных операторов из строки.

Заменили то, что я искал, одним символом и посчитали экземпляры одного символа.

Очевидно, что используемый вами символ необходимо проверить, чтобы он не существовал в строке, прежде чем это произойдет, чтобы избежать неправильного подсчета.

bizah
источник
2

Строка в строке:

Найти "и т.д." в ".. JD JD JD JD и т. Д. И т. Д. JDJDJDJDJDJDJDJD и т. Д."

var strOrigin = " .. JD JD JD JD etc. and etc. JDJDJDJDJDJDJDJD and etc.";
var searchStr = "etc";
int count = (strOrigin.Length - strOrigin.Replace(searchStr, "").Length)/searchStr.Length.

Проверьте работоспособность, прежде чем отбросить это как ненадежное / неуклюжее ...

user3090281
источник
2

Мой начальный дубль дал мне что-то вроде:

public static int CountOccurrences(string original, string substring)
{
    if (string.IsNullOrEmpty(substring))
        return 0;
    if (substring.Length == 1)
        return CountOccurrences(original, substring[0]);
    if (string.IsNullOrEmpty(original) ||
        substring.Length > original.Length)
        return 0;
    int substringCount = 0;
    for (int charIndex = 0; charIndex < original.Length; charIndex++)
    {
        for (int subCharIndex = 0, secondaryCharIndex = charIndex; subCharIndex < substring.Length && secondaryCharIndex < original.Length; subCharIndex++, secondaryCharIndex++)
        {
            if (substring[subCharIndex] != original[secondaryCharIndex])
                goto continueOuter;
        }
        if (charIndex + substring.Length > original.Length)
            break;
        charIndex += substring.Length - 1;
        substringCount++;
    continueOuter:
        ;
    }
    return substringCount;
}

public static int CountOccurrences(string original, char @char)
{
    if (string.IsNullOrEmpty(original))
        return 0;
    int substringCount = 0;
    for (int charIndex = 0; charIndex < original.Length; charIndex++)
        if (@char == original[charIndex])
            substringCount++;
    return substringCount;
}

Игла в подходе к стогу сена с использованием замены и деления дает 21+ секунд, тогда как это занимает около 15,2.

Отредактируйте после добавления немного, которое добавит substring.Length - 1к charIndex (как и должно быть), это на 11,6 секунды.

Редактировать 2: я использовал строку, которая имела 26 двухсимвольных строк, вот времена, обновленные к тем же образцам текста:

Игла в стоге сена (версия ОП): 7,8 секунды

Предлагаемый механизм: 4,6 секунды.

Редактировать 3: добавив односимвольный угловой регистр, это заняло 1,2 секунды.

Изменить 4: Для контекста: было использовано 50 миллионов итераций.

Аллен Кларк Коупленд-младший
источник
2

Думал, что я добавлю свой метод расширения в кольцо (см. Комментарии для получения дополнительной информации). Я не проводил никаких официальных тестов, но думаю, что для большинства сценариев это должно быть очень быстрым.

РЕДАКТИРОВАТЬ: ОК - так что этот SO вопрос заставил меня задуматься, как производительность нашей текущей реализации будет сочетаться с некоторыми решениями, представленными здесь. Я решил сделать небольшую оценку производительности и обнаружил, что наше решение очень соответствует производительности решения, предоставленного Ричардом Уотсоном. до тех пор, пока вы не начнете агрессивный поиск с большими строками (100 Кбайт +), большими подстроками (32 Кбайт +) ) и много встроенных повторений (10K +). На тот момент наше решение было примерно в 2-4 раза медленнее. Учитывая это и тот факт, что нам действительно нравится решение, представленное Ричардом Уотсоном, мы соответствующим образом реорганизовали наше решение. Я просто хотел сделать это доступным для всех, кто мог бы извлечь из этого пользу.

Наше оригинальное решение:

    /// <summary>
    /// Counts the number of occurrences of the specified substring within
    /// the current string.
    /// </summary>
    /// <param name="s">The current string.</param>
    /// <param name="substring">The substring we are searching for.</param>
    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
    /// should be aggressive in its search behavior (see Remarks). Default 
    /// behavior is non-aggressive.</param>
    /// <remarks>This algorithm has two search modes - aggressive and 
    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
    /// true), the algorithm will try to match at every possible starting 
    /// character index within the string. When false, all subsequent 
    /// character indexes within a substring match will not be evaluated. 
    /// For example, if the string was 'abbbc' and we were searching for 
    /// the substring 'bb', then aggressive search would find 2 matches 
    /// with starting indexes of 1 and 2. Non aggressive search would find 
    /// just 1 match with starting index at 1. After the match was made, 
    /// the non aggressive search would attempt to make it's next match 
    /// starting at index 3 instead of 2.</remarks>
    /// <returns>The count of occurrences of the substring within the string.</returns>
    public static int CountOccurrences(this string s, string substring, 
        bool aggressiveSearch = false)
    {
        // if s or substring is null or empty, substring cannot be found in s
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
            return 0;

        // if the length of substring is greater than the length of s,
        // substring cannot be found in s
        if (substring.Length > s.Length)
            return 0;

        var sChars = s.ToCharArray();
        var substringChars = substring.ToCharArray();
        var count = 0;
        var sCharsIndex = 0;

        // substring cannot start in s beyond following index
        var lastStartIndex = sChars.Length - substringChars.Length;

        while (sCharsIndex <= lastStartIndex)
        {
            if (sChars[sCharsIndex] == substringChars[0])
            {
                // potential match checking
                var match = true;
                var offset = 1;
                while (offset < substringChars.Length)
                {
                    if (sChars[sCharsIndex + offset] != substringChars[offset])
                    {
                        match = false;
                        break;
                    }
                    offset++;
                }
                if (match)
                {
                    count++;
                    // if aggressive, just advance to next char in s, otherwise, 
                    // skip past the match just found in s
                    sCharsIndex += aggressiveSearch ? 1 : substringChars.Length;
                }
                else
                {
                    // no match found, just move to next char in s
                    sCharsIndex++;
                }
            }
            else
            {
                // no match at current index, move along
                sCharsIndex++;
            }
        }

        return count;
    }

И вот наше исправленное решение:

    /// <summary>
    /// Counts the number of occurrences of the specified substring within
    /// the current string.
    /// </summary>
    /// <param name="s">The current string.</param>
    /// <param name="substring">The substring we are searching for.</param>
    /// <param name="aggressiveSearch">Indicates whether or not the algorithm 
    /// should be aggressive in its search behavior (see Remarks). Default 
    /// behavior is non-aggressive.</param>
    /// <remarks>This algorithm has two search modes - aggressive and 
    /// non-aggressive. When in aggressive search mode (aggressiveSearch = 
    /// true), the algorithm will try to match at every possible starting 
    /// character index within the string. When false, all subsequent 
    /// character indexes within a substring match will not be evaluated. 
    /// For example, if the string was 'abbbc' and we were searching for 
    /// the substring 'bb', then aggressive search would find 2 matches 
    /// with starting indexes of 1 and 2. Non aggressive search would find 
    /// just 1 match with starting index at 1. After the match was made, 
    /// the non aggressive search would attempt to make it's next match 
    /// starting at index 3 instead of 2.</remarks>
    /// <returns>The count of occurrences of the substring within the string.</returns>
    public static int CountOccurrences(this string s, string substring, 
        bool aggressiveSearch = false)
    {
        // if s or substring is null or empty, substring cannot be found in s
        if (string.IsNullOrEmpty(s) || string.IsNullOrEmpty(substring))
            return 0;

        // if the length of substring is greater than the length of s,
        // substring cannot be found in s
        if (substring.Length > s.Length)
            return 0;

        int count = 0, n = 0;
        while ((n = s.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
        {
            if (aggressiveSearch)
                n++;
            else
                n += substring.Length;
            count++;
        }

        return count;
    }
Кейси Честер
источник
1
string Name = "Very good nice one is very good but is very good nice one this is called the term";
bool valid=true;
int count = 0;
int k=0;
int m = 0;
while (valid)
{
    k = Name.Substring(m,Name.Length-m).IndexOf("good");
    if (k != -1)
    {
        count++;
        m = m + k + 4;
    }
    else
        valid = false;
}
Console.WriteLine(count + " Times accures");
Prashanth
источник
1
string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
int count = 0;
for (int i = 0; i < s.Length; i++)
   if (s[i] == 'H') count++;

Он просто проверяет каждый символ в строке, если символ является символом, который вы ищете, добавьте один для подсчета.

joppiesaus
источник
1

Если вы загляните на эту веб-страницу , 15 различных способов сделать это будут сопоставлены, в том числе с использованием параллельных циклов.

Самый быстрый способ - использовать однопоточный цикл for (если у вас версия .Net <4.0) или параллельный цикл for (если вы используете .Net> 4.0 с тысячами проверок).

Предполагая, что "ss" - это ваша строка поиска, "ch" - это ваш массив символов (если у вас есть более одного символа, который вы ищете), вот основная суть кода, который имел быструю однопоточную скорость выполнения:

for (int x = 0; x < ss.Length; x++)
{
    for (int y = 0; y < ch.Length; y++)
    {
        for (int a = 0; a < ss[x].Length; a++ )
        {
        if (ss[x][a] == ch[y])
            //it's found. DO what you need to here.
        }
    }
}

Исходный код теста также предоставляется, чтобы вы могли запускать свои собственные тесты.


источник
1
str="aaabbbbjjja";
int count = 0;
int size = str.Length;

string[] strarray = new string[size];
for (int i = 0; i < str.Length; i++)
{
    strarray[i] = str.Substring(i, 1);
}
Array.Sort(strarray);
str = "";
for (int i = 0; i < strarray.Length - 1; i++)
{

    if (strarray[i] == strarray[i + 1])
    {

        count++;
    }
    else
    {
        count++;
        str = str + strarray[i] + count;
        count = 0;
    }

}
count++;
str = str + strarray[strarray.Length - 1] + count;

Это для подсчета появления персонажа. Для этого примера выводом будет «a4b4j3»

Нарендра Кумар
источник
2
Не совсем «подсчет вхождений строки», более подсчет символов - как насчет способа указать, какой строке соответствовать Narenda?
Пол Салливан
1
int count = 0; string str = "у нас есть foo и foo, пожалуйста, подсчитайте foo в этом"; string stroccurance = "foo"; string [] strarray = str.Split (''); Array.sort (strarray); str = ""; for (int i = 0; i <strarray.Length - 1; i ++) {if (strarray [i] == stroccurance) {count ++; }} str = "Количество случаев для" + stroccurance + "равно" + count; Посредством этого вы можете сосчитать любое вхождение строки в этом примере. Я подсчитываю вхождение "foo", и оно даст мне вывод 3.
Нарендра Кумар
1

Для случая строкового разделителя (не для случая символа, как говорит субъект):
string source = "@@@ Once @@@ at @@@ a @@@ time @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries) .Length - 1;

Естественный разделитель исходного значения плаката ("/ Once / Upon / A / Time /") представляет собой символ '/', а ответы объясняют параметр source.Split (char []), хотя ...

Сэм Саарян
источник
0

использование System.Linq;

int CountOf => "A :: BC :: D" .Split ("::"). Длина - 1;

Соларев Сергей
источник