文字列内の文字列(実際にはchar)の出現をどのようにカウントしますか?


864

/文字列で何個のsを見つけることができるかを知りたいと思ったとき、いくつかの方法がありましたが、最良(または最も簡単)なものを決定できませんでした。 。

現時点では私は次のようなものを使っています:

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
実際のところ、「/」で除算する必要があります。長さ
ジェラール

3
「/////」内の「//」の発生数をカウントする必要があるとあなたの要件はどのように言うでしょうか?2または4?
2014年

1
正規表現を使用することはおそらくそれを行うための最良の方法です
アダム・ヒギンズ

回答:


1009

.NET 3.5を使用している場合は、LINQを使用してワンライナーでこれを行うことができます。

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

LINQを使用したくない場合は、次の方法で実行できます。

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

あなたの元のテクニックはこれらのどちらよりも約30%速いように思えることに驚かれるかもしれません!「/ once / upon / a / time /」で簡単なベンチマークを実行したところ、結果は次のとおりです。

元の= 12
秒の
ソース。カウント= 19秒のソース。スプリット= 17 秒の
foreach(ボブウィーンホルトの回答から)= 10秒

(時間は50,000,000回の反復のためのものなので、現実の世界で大きな違いに気付くことはほとんどありません。)


6
そう、VSは文字列クラスのLINQ拡張メソッドを隠しています。開発者がこれらすべての拡張メソッドを文字列クラスに表示することを望んでいないと彼らが考えたと思います。おそらく賢明な決定です。
ユダガブリエルヒマンゴ2009

11
この動作は、VS2010が新しいクラスファイルにSystem.Linqを自動的に含めるのが原因である可能性があります。インテリセンスを機能させるには、名前空間が必要です。
スプレーグ

30
CountおよびSplitソリューションは、文字を数える場合にのみ機能することに注意してください。OPのソリューションのように文字列を処理することはできません。
Peter Lillevold、2014年

5
f == '\' 文字列内の文字ではなく、文字列内の文字についてです
Thomas Weller '26

9
これは別の質問に対する答えのようです:「文字列内のcharの出現をどのようにカウントしますか?」
Ben Aaronson

181
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

source.Replace()それ自体よりも高速でなければなりません。


18
foreachの代わりにforに切り替えることで、わずかな改善が得られますが、ごくわずかです。
マーク・

17
いいえ。質問では、文字ではなく文字列の出現を数えるよう求められます。
YukiSakura

3
これは文字列内の文字を数えています。タイトルは、文字列内の文字列をカウントすることに関するものです
Thomas Weller '26

2
@Markはforループでテストしただけで、実際にはforeachを使用するよりも低速でした。境界チェックが原因である可能性がありますか?(時間は1.65秒でしたが、5ミルの反復では2.05でした。)
2016

4
質問は文字列内の文字列を求めていますが、OPが投稿した問題の例は実際には1文字だけです。目の前の問題に対処する。
チャド

136
int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;

8
+1-場合によっては、追加する必要がありますRegexOptions.IgnoreCase
TrueWill、2014年

3
これは信じられないほど低くありませんか?
Thomas Ayoub

4
正規表現のオーバーヘッドは理想的ではなく、「このために正規表現を掘り下げたくありません」
チャド

たくない場合がありますRegex.Escape(...)ので、new System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
barlop

2
文字だけでなく文字列も検索できるため、これを使用しました。
Jamesがインディで

86

文字だけでなく文字列全体を検索できるようにしたい場合:

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

「文字列内の各文字について、その文字から始まる文字列の残りを部分文字列として受け取ります。ターゲット文字列で始まる場合は数えます。」


1
与えられた説明よりも明確な方法でそれをどのように説明できるかわかりません。何が混乱していますか?
mqp 2012年

58
スーパースロー! htmlのページで試してみたところ、このページの他の方法では2秒かかっていたのに対し、約2分かかりました。正解でした。使用するには遅すぎた。
JohnB '20 / 06/20

2
同意した、遅すぎる。私はlinqスタイルのソリューションの大ファンですが、これは現実的ではありません。
スプレー

5
これが非常に遅い理由は、n個の文字列を作成するため、おおよそn ^ 2/2バイトを割り当てるためです。
Peter Crabtree

6
210000文字の文字列に対してOutOfMemoryExceptionがスローされます。
エンダー、

66

私はいくつかの調査を行ったところ、ほとんどの場合、Richard Watsonのソリューションが最も高速であることがわかりました。これは、投稿内のすべてのソリューションの結果の表です(「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|

短い文字列(10から50文字)で短い部分文字列(1から5文字)の出現回数を見つける場合は、元のアルゴリズムが優先されます。

また、複数文字の部分文字列の場合は、次のコードを使用する必要があります(Richard Watsonのソリューションに基づく)

int count = 0, n = 0;

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

私は自分の「低レベル」ソリューションを追加しようとしていました(部分文字列を作成せずに、置換/分割、または任意の正規表現/ Linqを使用せずに)。ありがとう!
Dan W

Regexソリューションの場合、次を追加しますRegex.Escape(needle)
Thymine 2013年

2
他の人に指摘するために、検索値が空の場合はチェックする必要があります。そうしないと、無限ループに入ります。
WhoIsRich 2014年

2
多分それは私だけかもしれませんが、source="aaa" substring="aa"私は1ではなく2を返すと予想していました。これを「修正」するには、次のように変更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を使用する価値は本当にありますか?カウントが整数を返さないものに置き換えられる可能性はありますか?
Whatsit 2009

69
@Whatsit: 'var'は左手だけで入力できますが、 'int'は両手が必要です;)
Sean Bright

7
int文字はすべてホームキーにありますが、ありvarません。ええと。待って、私はDvorakを使っています
Michael Buen

2
@BDotA 'using System.Linq;'があることを確認してください。ファイルの上部。また、intellisenseは文字列であるため、.Count呼び出しを非表示にする場合があります。それでも、コンパイルして問題なく実行できます。
ユダガブリエルヒマンゴ2012年

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

私のコンピューターでは、5,000万回の繰り返しで、文字ごとのソリューションよりも約2秒高速です。

2013年の改訂:

文字列をchar []に変更し、それを繰り返します。50m反復の合計時間をさらに1〜2秒短縮します。

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

私はなぜこれが可能でググリングしていたのか疑問に思っていました(リバース反復の方が速いということを思い出しました)。しかし、この文脈では逆転のトリックは新しいと思います。

C#で文字列内の個々の文字を反復処理する最も速い方法は何ですか?


1
あなたはwhileの括弧と括弧を入れsource.IndexOf('/', n + 1)たり失うことができn++ます:)また、string word = "/"文字の代わりに変数を入れます。
neeKo

1
こんにちは、新しい答えをチェックアウトしてください。ただし、可変長の部分文字列を作成するのは難しいかもしれません。
Richard Watson

私はsubtringをステップスルーすることによって同様のものを使用しました。これは、indexOfにstartIndexがあることに気づくまでです。速度とメモリフットプリントのバランスが取れているため、最初のソリューションが最も気に入っています。
Samir Banjanovic 2013

1
どこかで、値を0と比較する方が速いため、逆方向に反復する方が速いと読みました
reggaeguitar

1
@shitpoetうん。基礎となるコードを見ると、それはネイティブコールです。public char [] toCharArray(){... System.arraycopy(value、0、result、0、value.length); ...}
リチャードワトソン

46

これらはどちらも単一文字の検索語に対してのみ機能します...

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

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

長い針の方が良いかもしれません...

しかし、もっとエレガントな方法が必要です。:)


複数文字の置換を考慮するため。それがなければ、カウントは「」「テストはキーがある」6に戻ってくる
ZombieSheep

ベンチマークし、これをstring.Split-wayと比較しました-約1.5倍速く動作します。称賛。
Alex

20

編集:

source.Split('/').Length-1

2
これが私がすることです。そしてsource.Split(new[]{"//"}, StringSplitOptions.None).Count - 1、複数文字の区切り文字。
bzlm 2009年

4
これは、ヒープ上で少なくともn個の文字列割り当てを実行し、さらに(おそらく)いくつかの配列のサイズ変更を実行します。非常に非効率的で、適切にスケーリングされないため、重要なコードで使用しないでください。
Zar Shardan

17

C#では、素敵なString SubStringカウンターは、予期せぬトリッキーな仲間です。

public static int CCount(String haystack, String needle)
{
    return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}

1
素敵な解決策-(charだけでなく)文字列でも動作します!
ChriPf

おかげで、言語を交換するとき、文字列処理の微妙なことの一部を忘れるのは非常に簡単です-私たちのほとんどが最近持っているように!
Dave

1
-1の理由:Count()とCountまたはLengthの違いを知っていますか?誰かがCountまたはLengthの代わりにCount()を使用している場合、トリガーされます。Count()はIEnumeratorを作成してから、IEnumerableのすべての発生を通過しますが、CountまたはLengthは、すべての要素を反復処理する必要なく、必要な数をすでに保持しているオブジェクトのプロパティに既に設定されています。
エアロソン2017

良い点、そして奇妙なのは、私のライブラリでは、関数を実行したところから「長さ」を使用していることです。編集しました!
デイブ

15
Regex.Matches(input,  Regex.Escape("stringToMatch")).Count

1
入力に正規表現の特殊文字が含まれている場合、これは正しくありません。Regex.Escape(input)が必要です
Pedersen

1
実際には、stringToMatchニーズではありませんinput
Theodor Zoulias、

その通りその通り。修正しました。
cederlof

13
private int CountWords(string text, string word) {
    int count = (text.Length - text.Replace(word, "").Length) / word.Length;
    return count;
}

元の解決策はcharsの方が最速だったので、文字列の場合もそうなるでしょう。これが私の貢献です。

コンテキスト:ログファイルで「失敗」や「成功」などの単語を探していました。

Gr、ベン


2
"word"変数に空の文字列を渡さないでください(ゼロ除算エラー)。
アンドリューイェンス

12
string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
  count++;

20
またはRegex.Matches(s、 "65")。Count ^ _ ^
Meta

すべての文字列に対して機能するわけではありません。「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;
    }
}

渡された文字列がnullまたは空の場合、2番目のメソッドはうまくいきませんか?純粋にスタイルの観点から、入力を単なる文字列ではなくSystem.Stringとして定義していますか?
Nodoid

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

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;

3
string search = "/string";
var occurrences = (regex.Match(search, @"\/")).Count;

これは、プログラムが「/ s」を正確に検出するたびにカウントし(大文字と小文字を区別)、これの発生回数は変数「occurrences」に格納されます。


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」は文字列で「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

最後に、360万文字のファイルがありました。10万回繰り返された「derp adfderdserp dfaerpderp deasderp」でした。上記の方法でこれらの結果を100倍にして、ファイル内の「derp」を検索しました。

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

したがって、私の4番目の方法は確実に勝者ですが、現実的には、360万の文字ファイルが100回で1586ミリ秒しかかかっていない場合、これはすべて無視できます。

ちなみに、CountSubstrメソッドとCountCharメソッドを100回使用して、360万文字のファイルの「d」文字もスキャンしました。結果...

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

これによると、元のポスター方式は、大規模な干し草の山にある1文字の針には非常に適していません。

注:すべての値がリリースバージョンの出力に更新されました。初めてこれを投稿したとき、誤ってリリースモードを構築するのを忘れていました。私の声明の一部が修正されました。


パフォーマンス結果をありがとうございます。速度の10の因子の違いは、linqまたはその他の適切に記述されたソリューションを考慮せずに、拡張メソッドを使用する理由である可能性があります。
Andreas Reiff

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

Richard Watsonの回答のバリエーションで、文字列でcharが出現する回数が増えるほど効率が向上し、コードが少なくなります。

言うまでもありませんが、すべてのシナリオを広範囲にテストすることなく、以下を使用することで速度が大幅に向上しました。

int count = 0;
for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;

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

文字列から条件文をテストするのと同じようなことをする必要がありました。

私が探していたものを単一の文字に置き換え、単一の文字のインスタンスを数えました。

明らかに、これが発生する前に、使用している単一の文字が文字列に存在しないことを確認して、誤ったカウントを回避する必要があります。


2

文字列内の文字列:

「.. JD JD JD JDなどなどJDJDJDJDJDJDJDJDなど」で「etc」を検索します。

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.

これを不健全/不器用なものとして破棄する前にパフォーマンスを確認してください...


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 - 1charIndexした後(そうする必要があります)、は11.6秒です。

編集2:26文字の2文字の文字列を使用した文字列を使用しました。以下は、同じサンプルテキストに更新された時間です。

干し草の山の針(OPのバージョン):7.8秒

推奨されるメカニズム:4.6秒。

編集3:1文字のコーナーケースを追加すると、1.2秒になりました。

編集4:コンテキスト:5,000万回の反復が使用されました。


2

私は自分の拡張メソッドをリングに投げると思いました(詳細についてはコメントを参照してください)。正式なベンチマーキングは行っていませんが、ほとんどのシナリオで非常に高速でなければなりません。

編集:OK-それで、このSOの質問は私たちの現在の実装のパフォーマンスがここに提示されたいくつかのソリューションに対してどのようにスタックするのか疑問に思いました。少しベンチマークを付けることに決めたところ、私たちのソリューションはリチャードワトソンによって提供されたソリューションのパフォーマンスと非常に一致していることがわかりました 32 KB +(あなたが大規模な文字列(100 KB +)との積極的な検索を行っているまで、大部分文字列)および多くの埋め込まれた繰り返し(10K +)。その時点で、ソリューションは約2倍から4倍遅くなりました。これと、Richard Watsonによって提示されたソリューションが本当に気に入っているという事実を踏まえて、それに応じてソリューションをリファクタリングしました。私は、これから利益を得る可能性のあるすべての人がこれを利用できるようにしたかっただけです。

独自のソリューション:

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

1
string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
int count = 0;
for (int i = 0; i < s.Length; i++)
   if (s[i] == 'H') count++;

文字列内のすべての文字をチェックするだけです。その文字が検索している文字である場合は、1つ追加してカウントします。


1

このWebページ確認すると、並列ループの使用など、これを実行する15の異なる方法がベンチマークされています。

最速の方法は、シングルスレッドのforループ(.Netバージョン<4.0を使用している場合)またはparallel.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をカウントしてください"; 文字列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」というののoccuranceを数えています。この例では、任意の文字列のoccuranceをカウントすることができますし、それは私に出力3.与えるだろう
ナレンドラ・クマール

1

文字列区切り文字の場合(件名のとおり、文字の大文字小文字の区別はありません):
string source = "@@@ once @@@ upon @@@ a @@@ time @@@";
int count = source.Split(new [] {"@@@"}、StringSplitOptions.RemoveEmptyEntries).Length-1;

投稿者の元のソース値( "/ once / upon / a / time /")の自然区切り文字は文字「/」であり、応答はsource.Split(char [])オプションを説明しますが...


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.