Bagaimana Anda menghitung kemunculan string (sebenarnya char) dalam sebuah string?

865

Saya melakukan sesuatu di mana saya sadar saya ingin menghitung berapa / dapat saya temukan dalam sebuah string, dan kemudian saya tersadar, bahwa ada beberapa cara untuk melakukannya, tetapi tidak dapat memutuskan apa yang terbaik (atau yang paling mudah) adalah .

Saat ini saya akan dengan sesuatu seperti:

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

Tapi saya tidak suka sama sekali, tidak ada yang mengambil?

Saya benar-benar tidak ingin menggali RegEx untuk ini, bukan?

Saya tahu string saya akan memiliki istilah yang saya cari, sehingga Anda dapat menganggap bahwa ...

Tentu saja untuk string di mana panjang> 1 ,

string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;
meskipun
sumber
34
+1: saya harus mengatakan bahwa ini merupakan cara penghitungan yang sangat berbeda. saya terkejut dengan hasil tes tanda bangku :)
naveen
4
Ini tidak begitu berbeda ... itu cara yang khas untuk melaksanakan fungsi ini di SQL: LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N","")).
Sheridan
6
Faktanya, Anda harus membaginya dengan "/".Length
Gerard
3
Bolehkah saya bertanya, apa persyaratan Anda mengatakan bahwa penghitungan harus untuk jumlah kemunculan "//" dalam "/////"? 2 atau 4?
Les
1
menggunakan regex mungkin adalah cara terbaik untuk melakukannya
Adam Higgins

Jawaban:

1010

Jika Anda menggunakan .NET 3.5 Anda bisa melakukan ini dalam satu-liner dengan LINQ:

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

Jika Anda tidak ingin menggunakan LINQ Anda dapat melakukannya dengan:

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

Anda mungkin terkejut mengetahui bahwa teknik asli Anda tampaknya sekitar 30% lebih cepat daripada salah satu dari ini! Saya baru saja melakukan patokan cepat dengan "/ Once / upon / a / time /" dan hasilnya adalah sebagai berikut:


Sumber asli Anda = 12s. Jumlah = 19s sumber.
Plit = 17s
foreach ( dari jawaban bobwienholt ) = 10s

(Waktu untuk 50.000.000 iterasi sehingga Anda tidak akan melihat banyak perbedaan di dunia nyata.)

LukeH
sumber
6
Ya, VS menyembunyikan metode ekstensi LINQ pada kelas string. Saya kira mereka pikir devs tidak ingin semua metode ekstensi itu muncul di kelas string. Mungkin keputusan yang bijaksana.
Yehuda Gabriel Himango
11
Mungkin perilaku ini karena VS2010 secara otomatis menyertakan System.Linq dalam file kelas baru, VS2008 mungkin tidak. Namespace harus ada agar kecerdasan berfungsi.
Sprague
30
Perhatikan bahwa solusi Count and Split hanya akan berfungsi saat Anda menghitung karakter. Mereka tidak akan bekerja dengan string, seperti solusi OP.
Peter Lillevold
5
f == '\' adalah tentang karakter dalam string, bukan string dalam string
Thomas Weller
9
Ini sepertinya jawaban untuk pertanyaan yang berbeda: "Bagaimana Anda menghitung kemunculan char dalam string?"
Ben Aaronson
181
string source = "/once/upon/a/time/";
int count = 0;
foreach (char c in source) 
  if (c == '/') count++;

Harus lebih cepat daripada source.Replace()dengan sendirinya.

bobwienholt
sumber
18
Anda bisa mendapatkan peningkatan marjinal dengan beralih ke for bukan foreach, tetapi hanya sedikit, sangat kecil.
Mark
17
Tidak. Pertanyaannya meminta untuk menghitung kemunculan string, bukan karakter.
YukiSakura
3
Ini menghitung karakter dalam sebuah string. Judulnya adalah tentang menghitung string dalam sebuah string
Thomas Weller
2
@ Markus hanya mengujinya dengan for loop dan itu sebenarnya lebih lambat daripada menggunakan foreach. Bisa jadi karena pemeriksaan batas? (Waktu 1,65 detik vs 2,05 pada iterasi 5 mil.)
Mengukur
4
Sementara pertanyaannya adalah meminta string dalam string, contoh masalah yang diposting OP sebenarnya hanya satu karakter, dalam hal ini saya akan menyebut jawaban ini masih merupakan solusi yang valid, karena menunjukkan cara yang lebih baik (pencarian char daripada pencarian string) untuk mengatasi masalah yang dihadapi.
Chad
136
int count = new Regex(Regex.Escape(needle)).Matches(haystack).Count;
Namun Pembuat Kode Lain
sumber
8
+1 - Dalam beberapa kasus, Anda mungkin ingin menambahkan RegexOptions.IgnoreCase.
TrueWill
3
bukankah ini sangat rendah?
Thomas Ayoub
4
Regex overhead tidak ideal, ditambah "Saya tidak benar-benar ingin menggali RegEx untuk ini, bukan?"
Chad
mungkin tidak mau Regex.Escape(...)begitunew System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
barlop
2
Saya memilih yang ini karena bisa mencari string, bukan hanya karakter.
James in Indy
86

Jika Anda ingin dapat mencari seluruh string, dan bukan hanya karakter:

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

Baca sebagai "untuk setiap karakter dalam string, ambil sisa string mulai dari karakter itu sebagai substring; hitung jika dimulai dengan string target."

mqp
sumber
1
Tidak yakin bagaimana saya bisa menjelaskannya dengan cara yang lebih jelas daripada deskripsi yang diberikan. Apa yang membingungkan?
mqp
58
SUPER LAMBAT! Mencobanya pada halaman html dan butuh sekitar 2 menit dibandingkan metode lain pada halaman ini yang membutuhkan waktu 2 detik. Jawabannya benar; terlalu lambat untuk bisa digunakan.
JohnB
2
setuju, terlalu lambat. Saya penggemar berat solusi linq-style tapi yang ini tidak layak.
Sprague
5
Perhatikan bahwa alasan ini sangat lambat adalah karena ia menciptakan n string, sehingga mengalokasikan kira-kira n ^ 2/2 byte.
Peter Crabtree
6
OutOfMemoryException dilemparkan untuk 210000 karakter string saya.
ender
66

Saya telah melakukan beberapa penelitian dan menemukan bahwa solusi Richard Watson paling cepat dalam banyak kasus. Itulah tabel dengan hasil setiap solusi di posting (kecuali yang menggunakan Regex karena ia melempar pengecualian sambil mengurai string seperti "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|

Anda dapat melihat bahwa jika menemukan jumlah kemunculan substring pendek (1-5 karakter) dalam string pendek (10-50 karakter), algoritma asli lebih disukai.

Selain itu, untuk substring multicharacter Anda harus menggunakan kode berikut (berdasarkan solusi Richard Watson )

int count = 0, n = 0;

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

Saya akan menambahkan solusi 'tingkat rendah' ​​saya sendiri (tanpa membuat substring, menggunakan ganti / split, atau Regex / Linq apa pun), tetapi milik Anda mungkin lebih baik daripada milik saya (dan setidaknya lebih pendek). Terima kasih!
Dan W

Untuk solusi Regex, tambahkan aRegex.Escape(needle)
Thymine

2
Hanya untuk menunjukkan kepada orang lain, nilai pencarian perlu diperiksa jika kosong, jika tidak, Anda akan masuk ke loop tak terbatas.
WhoIsRich

2
Mungkin hanya saya, tetapi karena source="aaa" substring="aa"saya diharapkan untuk kembali 2, bukan 1. Untuk "memperbaiki" ini, ubah n += substring.Lengthken++
ytoledano

Anda dapat menambahkan overlappedbendera untuk memenuhi overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
kasing

54

LINQ berfungsi pada semua koleksi, dan karena string hanyalah kumpulan karakter, bagaimana dengan one-liner kecil yang menyenangkan ini:

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

Pastikan Anda memiliki using System.Linq;di bagian atas file kode Anda, seperti .Countmetode ekstensi dari namespace itu.


5
Apakah benar-benar layak menggunakan var di sana? Apakah ada kemungkinan Count akan diganti dengan sesuatu yang tidak menghasilkan int?
Whatsit

70
@ Whatsit: Anda dapat mengetik 'var' hanya dengan tangan kiri Anda sementara 'int' membutuhkan kedua tangan;)
Sean Bright

7
intsemua huruf berada di tombol home, sementara vartidak. uh .. tunggu, saya menggunakan Dvorak
Michael Buen

2
@BDotA Pastikan Anda memiliki 'using System.Linq;' di bagian atas file Anda. Intellisense juga dapat menyembunyikan panggilan .Count dari Anda karena ini adalah string. Meski begitu, itu akan dikompilasi dan berjalan dengan baik.
Yehuda Gabriel Himango

3
@JudahGabrielHimango Saya berpendapat bahwa var harus digunakan terutama ketika tipe variabelnya jelas (dan untuk singkatnya dan konsistensi)
EriF89

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

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

Di komputer saya ini sekitar 2 detik lebih cepat daripada solusi untuk setiap karakter untuk 50 juta iterasi.

Revisi 2013:

Ubah string menjadi char [] dan ulangi itu. Memotong satu atau dua detik lebih lanjut dari total waktu untuk iterasi 50m!

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

Ini masih lebih cepat:

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

Untuk ukuran yang baik, iterasi dari akhir array ke 0 tampaknya menjadi yang tercepat, sekitar 5%.

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

Saya bertanya-tanya mengapa ini bisa dan sedang Googling di sekitar (saya ingat sesuatu tentang membalikkan iterasi menjadi lebih cepat), dan menemukan pertanyaan SO yang mengganggu menggunakan string untuk teknik char [] sudah. Saya pikir trik pembalikan itu baru dalam konteks ini.

Apa cara tercepat untuk beralih melalui karakter individual dalam sebuah string dalam C #?


1
Anda bisa meletakkan source.IndexOf('/', n + 1)dan kehilangan n++tanda kurung sementara :) Juga, letakkan variabel string word = "/"sebagai ganti karakter.
neeKo

1
Hai Niko, cek jawaban baru. Akan lebih sulit untuk membuat substring panjang variabel.
Richard Watson

Saya menggunakan sesuatu yang serupa dengan melangkah melalui subtring; itu sampai saya menyadari indexOf memiliki startIndex. Saya paling suka solusi pertama karena ini adalah keseimbangan yang baik antara kecepatan dan jejak memori.
Samir Banjanovic

1
Saya membaca di suatu tempat bahwa lebih cepat untuk beralih ke belakang karena lebih cepat untuk membandingkan nilai ke 0
reggaeguitar

1
@shitpoet ya. Jika Anda melihat kode yang mendasarinya, itu adalah panggilan asli. karakter publik [] toCharArray () {... System.arraycopy (nilai, 0, hasil, 0, value.length); ...}

sumber
46

Keduanya hanya berfungsi untuk istilah pencarian satu karakter ...

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

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

mungkin menjadi lebih baik untuk jarum yang lebih panjang ...

Tetapi harus ada cara yang lebih elegan. :)

ZombieSheep
sumber
Untuk menjelaskan penggantian multi-karakter. Tanpa itu, menghitung "the" in "the test is the key" akan mengembalikan 6.
ZombieSheep
Diperbandingan & membandingkan ini dengan string. Meletakkan arah - bekerja sekitar 1,5 kali lebih cepat. Pujian.
Alex
20

Edit:

source.Split('/').Length-1
Brian Rudolph
sumber
2
Inilah yang saya lakukan. Dan source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1untuk pemisah multi-karakter.
bzlm
4
Ini akan melakukan setidaknya alokasi n string pada heap, ditambah (mungkin) beberapa ukuran array - dan semua ini hanya untuk mendapatkan hitungan? Sangat tidak efisien, tidak skala dengan baik dan tidak boleh digunakan dalam kode penting apa pun.
Zar Shardan
17

Di C #, penghitung String SubString yang bagus adalah orang yang sulitnya tak terduga ini:

public static int CCount(String haystack, String needle)
{
    return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}
Dave
sumber
1
Solusi yang bagus - dan bekerja untuk string juga (bukan hanya char)!
ChriPf
Terima kasih, terlalu mudah untuk melupakan beberapa seluk-beluk penanganan string saat bertukar bahasa - seperti kebanyakan dari kita sekarang ini!
Dave
1
-1 karena: Apakah Anda tahu perbedaan antara Count () dan Count atau Panjang? Jika seseorang menggunakan Count () alih-alih Count atau Panjang, saya akan terpicu. Count () menciptakan IEnumerator kemudian pergi melalui semua kejadian dari IEnumerable sedangkan Count atau Panjang sudah menetapkan properti objek yang sudah memegang jumlah yang Anda inginkan tanpa perlu mengulangi semua elemen.
aeroson
Tempat yang bagus, dan yang aneh adalah bahwa di perpustakaan saya, dari mana saya mengambil fungsi, saya menggunakan "Panjang". Diedit!
Dave
15
Regex.Matches(input,  Regex.Escape("stringToMatch")).Count
cederlof
sumber
1
Ini tidak benar jika input bertentangan dengan karakter khusus yaitu | Perlu ada Regex.Escape (input)
Esben Skov Pedersen
1
Sebenarnya stringToMatchkebutuhan melarikan diri, bukan input.
Theodor Zoulias
Kamu benar. Memperbaikinya.
cederlof
13
private int CountWords(string text, string word) {
    int count = (text.Length - text.Replace(word, "").Length) / word.Length;
    return count;
}

Karena solusi asli, adalah yang tercepat untuk karakter, saya kira itu juga untuk string. Jadi, inilah kontribusi saya.

Untuk konteksnya: Saya mencari kata-kata seperti 'gagal' dan 'berhasil' dalam file log.

Gr, Ben

Ben
sumber
2
Hanya saja, jangan melewatkan string kosong untuk variabel "kata" (pembagian dengan kesalahan nol).
Andrew Jens
12
string s = "65 fght 6565 4665 hjk";
int count = 0;
foreach (Match m in Regex.Matches(s, "65"))
  count++;
preetham
sumber
20
atau Regex.Matches (s, "65"). Hitung ^ _ ^
Meta
Bekerja bukan untuk setiap string. Coba cari "++" di "abc ++ def ++ xyz"
marsh-goyangkan
7

Bagi siapa pun yang menginginkan metode ekstensi String yang siap digunakan,

di sini adalah apa yang saya gunakan yang didasarkan pada yang terbaik dari jawaban yang diposting:

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
sumber
Bukankah metode kedua akan booming jika string yang dilewatkan adalah nol atau kosong? Murni dari sudut pandang gaya, apa yang Anda mendefinisikan input sebagai System.String daripada hanya 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;
}
pengguna460847
sumber
5

Saya pikir cara termudah untuk melakukan ini adalah dengan menggunakan Ekspresi Reguler. Dengan cara ini Anda bisa mendapatkan jumlah pemisahan yang sama seperti yang Anda bisa menggunakan myVar.Split ('x') tetapi dalam pengaturan beberapa karakter.

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
sumber
3
string search = "/string";
var occurrences = (regex.Match(search, @"\/")).Count;

Ini akan dihitung setiap kali program menemukan "/ s" tepat (case-sensitive) dan jumlah kemunculan ini akan disimpan dalam variabel "kemunculan"

Adam Higgins
sumber
3

Saya merasa bahwa kami kekurangan jenis penghitungan sub string tertentu, seperti perbandingan byte-by-byte yang tidak aman. Saya mengumpulkan metode poster asli dan metode apa pun yang dapat saya pikirkan.

Ini adalah ekstensi string yang saya buat.

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

Diikuti oleh kode tes ...

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

Hasil: CSX sesuai dengan CountSubstrX dan CCX sesuai dengan CountCharX. "chr" mencari string untuk '_', "dan" mencari string untuk "dan", dan "mlw" mencari string untuk "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

Dan akhirnya, saya punya file dengan 3,6 juta karakter. Itu "derp adfderdserp dfaerpderp deasderp" diulang 100.000 kali. Saya mencari "derp" di dalam file dengan metode di atas 100 kali hasil ini.

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

Jadi metode ke-4 saya pasti pemenangnya, tetapi, secara realistis, jika file 3,6 juta karakter 100 kali hanya mengambil 1586 ms sebagai kasus terburuk, maka semua ini cukup dapat diabaikan.

Ngomong-ngomong, saya juga memindai 'd' char dalam file 3,6 juta karakter dengan 100 kali metode CountSubstr dan CountChar. Hasil ...

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

Metode poster asli sangat buruk untuk jarum karakter tunggal di tumpukan jerami besar menurut ini.

Catatan: Semua nilai diperbarui ke Keluaran versi keluaran. Saya tidak sengaja lupa untuk membangun mode Rilis setelah pertama kali saya memposting ini. Beberapa pernyataan saya telah diubah.

Nicholas R. Grant
sumber
Terima kasih atas hasil kinerja. Perbedaan faktor dalam kecepatan 10 mungkin menjadi alasan untuk tidak mempertimbangkan LINQ atau solusi tertulis lainnya tetapi pergi dengan metode ekstensi.
Andreas Reiff
2

Fungsi generik untuk kemunculan string:

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;
}
Stefanos Kargas
sumber
2
Ini menciptakan sejumlah besar string sementara dan membuat pengumpul sampah bekerja sangat keras.
EricLaw
2
string source = "/once/upon/a/time/";
int count = 0, n = 0;
while ((n = source.IndexOf('/', n) + 1) != 0) count++;

Variasi pada jawaban Richard Watson, sedikit lebih cepat dengan peningkatan efisiensi semakin banyak kali karakter muncul dalam string, dan semakin sedikit kode!

Meskipun saya harus mengatakan, tanpa menguji setiap skenario secara ekstensif, saya memang melihat peningkatan kecepatan yang sangat signifikan dengan menggunakan:

int count = 0;
for (int n = 0; n < source.Length; n++) if (source[n] == '/') count++;
pengguna2011559
sumber
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;
            }

Diperlukan untuk melakukan sesuatu yang mirip dengan menguji pernyataan bersyarat dari string.

Mengganti apa yang saya cari dengan satu karakter dan menghitung instance dari karakter tunggal.

Jelas karakter tunggal yang Anda gunakan harus diperiksa untuk tidak ada dalam string sebelum ini terjadi untuk menghindari penghitungan yang salah.

bizah
sumber
2

String dalam string:

Temukan "dll" di ".. JD JD JD JD dll. Dan lain-lain. JDJDJDJDJDJDJDJD dan sebagainya."

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.

Periksa kinerja sebelum membuang yang ini sebagai tidak sehat / canggung ...

pengguna3090281
sumber
2

Pengambilan awal saya memberi saya sesuatu seperti:

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

Jarum dalam pendekatan tumpukan jerami menggunakan penggantian dan pembagian menghasilkan 21+ detik sedangkan ini membutuhkan waktu sekitar 15,2.

Edit setelah menambahkan sedikit yang akan ditambahkan substring.Length - 1 ke charIndex (seperti seharusnya), itu di 11,6 detik.

Sunting 2: Saya menggunakan string yang memiliki 26 string dua karakter, berikut adalah waktu yang diperbarui ke teks sampel yang sama:

Jarum di tumpukan jerami (versi OP): 7,8 Detik

Mekanisme yang disarankan: 4,6 detik.

Sunting 3: Menambahkan huruf sudut karakter tunggal, ia pergi ke 1,2 detik.

Sunting 4: Untuk konteks: 50 juta iterasi digunakan.

Allen Clark Copeland Jr
sumber
2

Saya pikir saya akan membuang metode ekstensi saya ke ring (lihat komentar untuk info lebih lanjut). Saya belum melakukan penandaan bangku formal, tapi saya pikir itu harus sangat cepat untuk sebagian besar skenario.

EDIT: OK - jadi pertanyaan SO ini membuat saya bertanya-tanya bagaimana kinerja implementasi kami saat ini akan menumpuk terhadap beberapa solusi yang disajikan di sini. Saya memutuskan untuk melakukan penilaian bangku kecil dan menemukan bahwa solusi kami sangat sejalan dengan kinerja solusi yang disediakan oleh Richard Watson hingga Anda melakukan pencarian agresif dengan string besar (100 Kb +), substring besar (32 Kb + ) dan banyak pengulangan tertanam (10K +). Pada saat itu solusi kami sekitar 2X hingga 4X lebih lambat. Mengingat ini dan fakta bahwa kami benar-benar menyukai solusi yang disajikan oleh Richard Watson, kami telah refactored solusi kami sesuai. Saya hanya ingin membuat ini tersedia untuk siapa saja yang mungkin mendapat manfaat dari itu.

Solusi asli kami:

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

Dan inilah solusi kami yang telah direvisi:

    /// <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;
    }
Casey Chester
sumber
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
sumber
1
string s = "HOWLYH THIS ACTUALLY WORKSH WOWH";
int count = 0;
for (int i = 0; i < s.Length; i++)
   if (s[i] == 'H') count++;

Itu hanya memeriksa setiap karakter dalam string, jika karakter adalah karakter yang Anda cari, tambahkan satu untuk dihitung.

joppiesaus
sumber
1

Jika Anda melihat halaman web ini , 15 cara berbeda untuk melakukan benchmark ini, termasuk menggunakan loop paralel.

Cara tercepat tampaknya menggunakan salah satu ulir tunggal untuk-loop (jika Anda memiliki .Net versi <4.0) atau parallel.for loop (jika menggunakan .Net> 4.0 dengan ribuan pemeriksaan).

Dengan asumsi "ss" adalah String Pencarian Anda, "ch" adalah array karakter Anda (jika Anda memiliki lebih dari satu karakter yang Anda cari), inilah inti dasar dari kode yang memiliki waktu menjalankan tercepat single threaded:

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

Kode sumber patokan disediakan juga sehingga Anda dapat menjalankan tes Anda sendiri.


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

Ini untuk menghitung terjadinya karakter. Untuk contoh ini output akan menjadi "a4b4j3"

Narendra Kumar
sumber
2
Tidak cukup 'menghitung kemunculan string' lebih banyak karakter penghitungan - bagaimana dengan cara menentukan string yang cocok dengan Narenda?
Paul Sullivan
1
int count = 0; string str = "kami memiliki foo dan foo tolong hitung foo di ini"; 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 = "Jumlah kemunculan untuk" + stroccurance + "adalah" + count; Melalui ini Anda dapat menghitung sembarang string dalam contoh ini saya menghitung terjadinya "foo" dan itu akan memberi saya output 3.
Narendra Kumar
1

Untuk kasus pembatas string (bukan untuk kasus char, seperti yang dikatakan subjek):
string source = "@@@ once @@@ pada @@@ a @@@ time @@@";
int count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries). Panjang - 1;

Pembatas alami nilai asli poster itu ("/ once / upon / a / time /") adalah char '/' dan responsnya menjelaskan opsi source.plit (char []) ...

Sam Saarian
sumber
0

menggunakan System.Linq;

int CountOf => "A :: BC :: D" .Split ("::"). Panjang - 1;

Solarev Sergey
sumber