Làm thế nào bạn sẽ đếm sự xuất hiện của một chuỗi (thực sự là một char) trong một chuỗi?


864

Tôi đang làm một cái gì đó mà tôi nhận ra rằng tôi muốn đếm /xem tôi có thể tìm thấy bao nhiêu chuỗi trong một chuỗi, và sau đó, tôi nhận ra rằng có một số cách để làm điều đó, nhưng không thể quyết định xem cái gì là tốt nhất (hoặc dễ nhất) .

Hiện tại tôi đang đi với một cái gì đó như:

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

Nhưng tôi không thích nó chút nào, có ai nhận không?

Tôi không thực sự muốn khai thác RegExcho điều này, phải không?

Tôi biết chuỗi của tôi sẽ có thuật ngữ tôi đang tìm kiếm, vì vậy bạn có thể cho rằng ...

Tất nhiên đối với các chuỗi độ dài> 1 ,

string haystack = "/once/upon/a/time";
string needle = "/";
int needleCount = ( haystack.Length - haystack.Replace(needle,"").Length ) / needle.Length;

34
+1: tôi phải nói rằng đó là một cách tính rất khác. tôi ngạc nhiên với kết quả kiểm tra điểm chuẩn :)
naveen

4
Nó không quá khác biệt ... đó là cách điển hình để thực hiện chức năng này trong SQL : LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N","")).
Sheridan

6
Vì thực tế, bạn nên chia cho "/".Lipse
Gerard

3
Tôi có thể hỏi, những yêu cầu của bạn sẽ nói số lượng nên là bao nhiêu cho số lần xuất hiện của "//" trong "/////"? 2 hay 4?
Les

1
sử dụng regex có lẽ là cách tốt nhất để thực hiện điều đó
Adam Higgins

Câu trả lời:


1009

Nếu bạn đang sử dụng .NET 3.5, bạn có thể thực hiện việc này trong một lớp lót với LINQ:

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

Nếu bạn không muốn sử dụng LINQ, bạn có thể làm điều đó với:

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

Bạn có thể ngạc nhiên khi biết rằng kỹ thuật ban đầu của bạn dường như nhanh hơn khoảng 30% so với một trong hai kỹ thuật này! Tôi vừa thực hiện một điểm chuẩn nhanh với "/ một lần / khi / a / lần /" và kết quả như sau:

Bản gốc = 12s
source.Count = 19s
source.Split = 17s
foreach ( từ câu trả lời của bobwienholt ) = 10s

(Thời gian dành cho 50.000.000 lần lặp để bạn không nhận thấy nhiều sự khác biệt trong thế giới thực.)


6
Vâng, VS ẩn các phương thức mở rộng LINQ trên lớp chuỗi. Tôi đoán họ đã tìm ra các nhà phát triển sẽ không muốn tất cả các phương thức mở rộng đó hiển thị trên lớp chuỗi. Có lẽ là một quyết định sáng suốt.
Judah Gabriel Himango

11
Có thể hành vi này là do VS2010 tự động bao gồm System.Linq trong các tệp lớp mới, VS2008 có thể không. Không gian tên cần phải có để intellisense hoạt động.
Sprague

30
Lưu ý rằng các giải pháp Đếm và Chia sẽ chỉ hoạt động khi bạn đếm các ký tự. Chúng sẽ không hoạt động với các chuỗi, giống như giải pháp của OP.
Peter Lillevold

5
f == '\' là về ký tự trong một chuỗi, không phải chuỗi trong chuỗi
Thomas Weller

9
Điều này có vẻ giống như câu trả lời cho một câu hỏi khác: "Làm thế nào bạn sẽ đếm số lần xuất hiện của một char trong một chuỗi?"
Ben Aaronson

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

Phải nhanh hơn source.Replace()chính nó.


18
Bạn có thể đạt được một sự cải thiện biên bằng cách chuyển sang một thay vì một foreach, nhưng chỉ một chút nhỏ xíu.
Đánh dấu

17
Không. Câu hỏi yêu cầu đếm sự xuất hiện của chuỗi, không phải ký tự.
YukiSakura

3
Đây là đếm các ký tự trong một chuỗi. Tiêu đề là về việc đếm các chuỗi trong một chuỗi
Thomas Weller

2
@Mark Chỉ cần thử nghiệm nó với một vòng lặp for và nó thực sự chậm hơn so với sử dụng foreach. Có thể là do kiểm tra giới hạn? (Thời gian là 1,65 giây so với 2,05 trên 5 triệu lần lặp.)
Đo

4
Trong khi câu hỏi đang yêu cầu một chuỗi trong một chuỗi, thì vấn đề ví dụ OP đăng thực ra chỉ là một ký tự, trong trường hợp đó tôi sẽ gọi câu trả lời này vẫn là một giải pháp hợp lệ, vì nó cho thấy một cách tốt hơn (tìm kiếm char thay vì tìm kiếm chuỗi) để giải quyết vấn đề trong tầm tay.
Chad

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

8
+1 - Trong một số trường hợp bạn có thể muốn thêm RegexOptions.IgnoreCase.
TrueWill

3
Đây không phải là cực kỳ thấp?
Thomas Ayoub

4
Chi phí hoạt động của Regex không lý tưởng, cộng với "Tôi không thực sự muốn khai thác RegEx cho việc này, phải không?"
Chad

có thể không muốn Regex.Escape(...)như vậynew System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
barlop

2
Tôi đã đi với cái này bởi vì nó có thể tìm kiếm các chuỗi, không chỉ các ký tự.
James ở Indy

86

Nếu bạn muốn có thể tìm kiếm toàn bộ chuỗi, và không chỉ các ký tự:

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

Đọc là "cho mỗi ký tự trong chuỗi, lấy phần còn lại của chuỗi bắt đầu từ ký tự đó làm chuỗi con; đếm nó nếu nó bắt đầu bằng chuỗi đích."


1
Không chắc chắn làm thế nào tôi có thể giải thích nó một cách rõ ràng hơn so với mô tả được đưa ra. Có gì khó hiểu?
mqp

58
SIÊU NHANH! Đã thử nó trên một trang html và mất khoảng 2 phút so với các phương pháp khác trên trang này mất 2 giây. Câu trả lời đã đúng; nó quá chậm để có thể sử dụng
JohnB

2
đồng ý, quá chậm. Tôi là một fan hâm mộ lớn của các giải pháp theo phong cách linq nhưng điều này chỉ là không khả thi.
Sprague

5
Lưu ý rằng lý do này rất chậm là vì nó tạo ra n chuỗi, do đó phân bổ khoảng n ^ 2/2 byte.
Peter Crabtree

6
OutOfMemoryException được ném cho 210000 ký tự chuỗi của tôi.
Ender

66

Tôi đã thực hiện một số nghiên cứu và thấy rằng giải pháp của Richard Watson là nhanh nhất trong hầu hết các trường hợp. Đó là bảng có kết quả của mọi giải pháp trong bài đăng (ngoại trừ những người sử dụng Regex vì nó đưa ra các ngoại lệ trong khi phân tích chuỗi như "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|

Bạn có thể thấy rằng trong trường hợp tìm thấy số lần xuất hiện của chuỗi con ngắn (1-5 ký tự) trong chuỗi ngắn (10-50 ký tự), thuật toán ban đầu được ưu tiên.

Ngoài ra, đối với chuỗi con đa vi khuẩn, bạn nên sử dụng mã sau (dựa trên giải pháp của Richard Watson )

int count = 0, n = 0;

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

Tôi đã định thêm giải pháp 'cấp thấp' của riêng mình (không tạo các chuỗi con, sử dụng thay thế / tách hoặc bất kỳ Regex / Linq nào), nhưng của bạn thậm chí còn tốt hơn của tôi (và ít nhất là ngắn hơn). Cảm ơn!
Dan W

Đối với các giải pháp Regex, hãy thêm vàoRegex.Escape(needle)
Thymine

2
Chỉ cần chỉ ra cho người khác, giá trị tìm kiếm cần được kiểm tra nếu trống, nếu không bạn sẽ vào một vòng lặp vô hạn.
WhoIsRich

2
Có thể đó chỉ là tôi, nhưng vì source="aaa" substring="aa"tôi dự kiến ​​sẽ lấy lại 2 chứ không phải 1. Để "sửa" cái này, hãy đổi n += substring.Lengththànhn++
ytoledano

bạn có thể thêm overlappedcờ để gặp trường hợp của bạn như thế này:overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
tsionyx

54

LINQ hoạt động trên tất cả các bộ sưu tập và vì các chuỗi chỉ là một bộ sưu tập các ký tự, làm thế nào về cái lót nhỏ xinh này:

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

Hãy chắc chắn rằng bạn có using System.Linq;ở đầu tệp mã của mình, như .Countlà một phương thức mở rộng từ không gian tên đó.


5
Có thực sự đáng sử dụng var ở đó? Có bất kỳ cơ hội nào Count sẽ được thay thế bằng thứ gì đó không trả về int không?
Whatsit

69
@Whatsit: bạn có thể nhập 'var' chỉ bằng tay trái trong khi 'int' yêu cầu cả hai tay;)
Sean Bright

7
inttất cả các chữ cái nằm trong phím home, trong khi varkhông. uh .. chờ đã, tôi đang sử dụng Dvorak
Michael Buen

2
@BDotA Đảm bảo bạn có 'sử dụng System.Linq;' ở đầu tập tin của bạn Ngoài ra, intellisense có thể ẩn cuộc gọi .Count khỏi bạn vì đó là một chuỗi. Mặc dù vậy, nó sẽ biên dịch và chạy tốt.
Judah Gabriel Himango

3
@JudahGabrielHimango Tôi sẽ lập luận rằng var nên được sử dụng đặc biệt là khi loại biến là rõ ràng (và cho ngắn gọn và nhất quán)
EriF89

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

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

Trên máy tính của tôi, nó nhanh hơn khoảng 2 giây so với giải pháp cho mỗi ký tự cho 50 triệu lần lặp.

Sửa đổi năm 2013:

Thay đổi chuỗi thành char [] và lặp qua đó. Giảm thêm một hoặc hai giây trong tổng thời gian cho 50m lặp lại!

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

Việc này vẫn nhanh hơn:

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

Đối với biện pháp tốt, lặp từ cuối mảng đến 0 dường như là nhanh nhất, khoảng 5%.

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

Tôi đã tự hỏi tại sao điều này có thể và là Googling xung quanh (tôi nhớ lại một vài điều về việc lặp lại nhanh hơn), và đã xuất hiện câu hỏi SO này sử dụng kỹ thuật chuỗi để char [] một cách khó chịu. Tôi nghĩ rằng thủ thuật đảo ngược là mới trong bối cảnh này, mặc dù.

Cách nhanh nhất để lặp qua các ký tự riêng lẻ trong một chuỗi trong C # là gì?


1
Bạn có thể đặt source.IndexOf('/', n + 1)và mất n++dấu ngoặc trong khi đó :) Ngoài ra, hãy đặt một biến string word = "/"thay vì ký tự.
neeKo

1
Này Niko, kiểm tra câu trả lời mới. Có thể khó hơn để tạo chuỗi con có độ dài thay đổi, mặc dù.
Richard Watson

Tôi đã sử dụng một cái gì đó tương tự bằng cách bước qua phần phụ; đó là cho đến khi tôi nhận ra indexOf có start Index. Tôi thích giải pháp đầu tiên nhất vì nó là sự cân bằng tốt giữa tốc độ và dấu chân bộ nhớ.
Samir Banjanovic

1
Tôi đã đọc ở đâu đó rằng việc lặp lại nhanh hơn vì việc so sánh giá trị với 0
reggaeg Ức

1
@shitpoet yup. Nếu bạn nhìn vào mã bên dưới, đó là một cuộc gọi riêng. công khai char [] toCharArray () {... System.arraycopy (value, 0, result, 0, value.length); ...}
Richard Watson

46

Cả hai chỉ hoạt động cho các cụm từ tìm kiếm một ký tự ...

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

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

có thể tốt hơn cho kim dài hơn ...

Nhưng phải có một cách thanh lịch hơn. :)


Để chiếm tài khoản thay thế nhiều ký tự. Nếu không có nó, việc đếm "the" trong bài kiểm tra là chìa khóa "sẽ trở lại 6.
ZombieSheep

Điểm chuẩn & so sánh điều này với chuỗi.Split-way - hoạt động nhanh hơn khoảng 1,5 lần. Thanh danh.
Alex

20

Biên tập:

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

2
Đây là những gì tôi làm. Và source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1cho dấu phân cách đa ký tự.
bzlm

4
Điều này sẽ thực hiện ít nhất n phân bổ chuỗi trên heap, cộng với (có thể) một vài kích thước lại mảng - và tất cả điều này chỉ để lấy số lượng? Vô cùng kém hiệu quả, không có quy mô tốt và không bao giờ nên được sử dụng trong bất kỳ mã quan trọng nào.
Zar Shardan

17

Trong C #, một bộ đếm Chuỗi SubString đẹp là người bạn khó tính bất ngờ này:

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

1
Giải pháp tuyệt vời - và làm việc cho chuỗi quá (không chỉ char)!
ChriPf

Cảm ơn, thật dễ dàng để quên một số sự tinh tế trong xử lý chuỗi khi hoán đổi ngôn ngữ - như hầu hết chúng ta phải làm trong những ngày này!
Dave

1
-1 vì: Bạn có biết sự khác biệt giữa Đếm () và Đếm hoặc Độ dài không? Nếu ai đó đang sử dụng Count () thay vì Count hoặc length tôi sẽ được kích hoạt. Count () tạo IEnumerator sau đó chuyển qua tất cả các lần xuất hiện của IEnumerable trong khi Count hoặc length là các thuộc tính đã được đặt của đối tượng đã giữ số lượng bạn muốn mà không cần phải lặp qua tất cả các phần tử.
aeroson

Điểm hay, và điều kỳ lạ là trong thư viện của tôi, từ nơi tôi nhận chức năng, tôi đang sử dụng "Độ dài". Đã chỉnh sửa!
Dave

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

1
Điều này là không chính xác nếu đầu vào regex chex đặc biệt là ký tự | Cần phải có Regex.Escape (đầu vào)
Esben Skov Pedersen

1
Trên thực tế các stringToMatchnhu cầu thoát, không phải input.
Theodor Zoulias

Bạn đúng. Đã sửa nó.
nhượng lại

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

Bởi vì giải pháp ban đầu, là nhanh nhất cho ký tự, tôi cho rằng nó cũng sẽ dành cho chuỗi. Vì vậy, đây là đóng góp của tôi.

Đối với ngữ cảnh: Tôi đã tìm kiếm các từ như 'thất bại' và 'thành công' trong một tệp nhật ký.

Gr, Ben


2
Chỉ không vượt qua một chuỗi trống cho biến "từ" (chia cho lỗi không).
Andrew Jens

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

20
hoặc Regex.Matches (s, "65"). Đếm ^ _ ^
Meta

Hoạt động không phải cho mọi chuỗi. Hãy thử tìm kiếm "++" trong "abc ++ def ++ xyz"
marsh-ngọ nguậy

7

Đối với bất kỳ ai muốn sẵn sàng sử dụng phương thức mở rộng Chuỗi,

đây là những gì tôi sử dụng dựa trên câu trả lời hay nhất được đăng:

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

Phương thức thứ hai sẽ bùng nổ nếu chuỗi được truyền vào là null hoặc rỗng? Hoàn toàn từ quan điểm phong cách, bạn định nghĩa đầu vào là System.String chứ không chỉ là chuỗi?
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

Tôi nghĩ cách dễ nhất để làm điều này là sử dụng Biểu thức chính quy. Bằng cách này, bạn có thể có được số lượng phân chia giống như bạn có thể sử dụng myVar.Split ('x') nhưng trong cài đặt nhiều ký tự.

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;

Điều này sẽ tính mỗi lần chương trình tìm thấy "/ s" chính xác (phân biệt chữ hoa chữ thường) và số lần xuất hiện của điều này sẽ được lưu trong biến "lần xuất hiện"


3

Tôi cảm thấy rằng chúng tôi đang thiếu một số loại đếm chuỗi phụ nhất định, như so sánh từng byte không an toàn. Tôi kết hợp phương pháp của người gửi ban đầu và bất kỳ phương pháp nào tôi có thể nghĩ ra.

Đây là những phần mở rộng chuỗi tôi đã thực hiện.

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

Tiếp theo là mã kiểm tra ...

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

Kết quả: CSX tương ứng với CountSubstrX và CCX tương ứng với CountCharX. "chr" tìm kiếm một chuỗi cho '_', "và" tìm kiếm một chuỗi cho "và" và "mlw" tìm kiếm một chuỗi cho "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

Và cuối cùng, tôi đã có một tập tin với 3,6 triệu ký tự. Đó là "derp adfderdserp dfaerpderp deasderp" được lặp lại 100.000 lần. Tôi đã tìm kiếm "derp" bên trong tệp với các phương pháp trên 100 lần các kết quả này.

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

Vì vậy, phương pháp thứ 4 của tôi chắc chắn là người chiến thắng, nhưng thực tế, nếu một tệp 3,6 triệu ký tự 100 lần chỉ mất 1586ms là trường hợp xấu hơn, thì tất cả điều này là không đáng kể.

Nhân tiện, tôi cũng đã quét char 'd' trong tệp 3,6 triệu ký tự với 100 lần phương thức CountSubstr và CountChar. Các kết quả...

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

Phương pháp áp phích ban đầu là rất xấu đối với kim nhân vật đơn trong một đống cỏ lớn theo điều này.

Lưu ý: Tất cả các giá trị đã được cập nhật thành phiên bản phát hành. Tôi đã vô tình quên xây dựng trên chế độ Phát hành ngay lần đầu tiên tôi đăng bài này. Một số tuyên bố của tôi đã được sửa đổi.


Cảm ơn bạn cho kết quả hiệu suất. Sự khác biệt về yếu tố tốc độ 10 có thể là một lý do để không xem xét một linq hoặc giải pháp được viết gọn gàng khác mà đi với một phương pháp mở rộng.
Andreas Reiff

2

Một hàm chung cho sự xuất hiện của chuỗi:

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
Điều này tạo ra một số lượng lớn các chuỗi tạm thời và làm cho trình thu gom rác hoạt động rất chăm chỉ.
EricLaw

2
string source = "/once/upon/a/time/";
int count = 0, n = 0;
while ((n = source.IndexOf('/', n) + 1) != 0) count++;

Một biến thể về câu trả lời của Richard Watson, nhanh hơn một chút với việc cải thiện hiệu quả càng nhiều lần char xuất hiện trong chuỗi và ít mã hơn!

Mặc dù tôi phải nói rằng, không cần thử nghiệm rộng rãi mọi kịch bản, tôi đã thấy sự cải thiện tốc độ rất đáng kể bằng cách sử dụng:

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

Cần phải làm một cái gì đó tương tự như kiểm tra các câu điều kiện từ một chuỗi.

Thay thế những gì tôi đang tìm kiếm bằng một ký tự và đếm các thể hiện của ký tự đơn.

Rõ ràng là một ký tự bạn đang sử dụng sẽ cần phải được kiểm tra để không tồn tại trong chuỗi trước khi điều này xảy ra để tránh đếm sai.


2

Chuỗi trong chuỗi:

Tìm "vv" trong ".. JD JD JD JD, v.v. và JDJDJDJDJDJDJDJD, v.v."

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.

Kiểm tra hiệu suất trước khi loại bỏ cái này là không chắc chắn / vụng về ...


2

Việc ban đầu của tôi đã cho tôi một cái gì đó như:

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

Kim trong cách tiếp cận haystack sử dụng thay thế và phân chia mang lại hơn 21 giây trong khi điều này mất khoảng 15.2.

Chỉnh sửa sau khi thêm một chút sẽ thêm substring.Length - 1 vào char Index (giống như vậy), đó là 11,6 giây.

Chỉnh sửa 2: Tôi đã sử dụng một chuỗi có 26 chuỗi hai ký tự, đây là thời gian được cập nhật cho cùng một văn bản mẫu:

Cây kim trong đống cỏ khô (phiên bản của OP): 7,8 giây

Cơ chế đề xuất: 4,6 giây.

Chỉnh sửa 3: Thêm trường hợp góc nhân vật duy nhất, nó đã đi đến 1,2 giây.

Chỉnh sửa 4: Đối với bối cảnh: 50 triệu lần lặp đã được sử dụng.


2

Nghĩ rằng tôi sẽ ném phương thức mở rộng của mình vào vòng (xem bình luận để biết thêm thông tin). Tôi chưa thực hiện bất kỳ đánh dấu băng ghế chính thức nào, nhưng tôi nghĩ nó phải rất nhanh đối với hầu hết các kịch bản.

EDIT: OK - vì vậy câu hỏi SO này khiến tôi tự hỏi làm thế nào hiệu suất của việc triển khai hiện tại của chúng tôi sẽ chồng lên một số giải pháp được trình bày ở đây. Tôi quyết định làm một ít đánh dấu băng ghế và thấy rằng giải pháp của chúng tôi rất phù hợp với hiệu suất của giải pháp được cung cấp bởi Richard Watson cho đến khi bạn thực hiện tìm kiếm tích cực với chuỗi lớn (100 Kb +), đế lớn (32 Kb + ) và nhiều lần lặp lại nhúng (10K +). Tại thời điểm đó, giải pháp của chúng tôi chậm hơn khoảng 2 lần đến 4 lần. Với điều này và thực tế là chúng tôi thực sự thích giải pháp được trình bày bởi Richard Watson, chúng tôi đã tái cấu trúc giải pháp của chúng tôi cho phù hợp. Tôi chỉ muốn làm điều này có sẵn cho bất cứ ai có thể hưởng lợi từ nó.

Giải pháp ban đầu của chúng tôi:

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

Và đây là giải pháp sửa đổi của chúng tôi:

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

Nó chỉ kiểm tra mọi ký tự trong chuỗi, nếu ký tự đó là ký tự bạn đang tìm kiếm, hãy thêm một ký tự để đếm.


1

Nếu bạn kiểm tra trang web này , 15 cách khác nhau để làm điều này được điểm chuẩn, bao gồm sử dụng các vòng lặp song song.

Cách nhanh nhất dường như là sử dụng một vòng lặp for-thread đơn (nếu bạn có phiên bản .Net <4.0) hoặc vòng lặp song song.cho (nếu sử dụng .Net> 4.0 với hàng ngàn kiểm tra).

Giả sử "ss" là Chuỗi tìm kiếm của bạn, "ch" là mảng ký tự của bạn (nếu bạn có nhiều char bạn đang tìm), đây là ý chính cơ bản của mã có thời gian chạy nhanh nhất trong chuỗi:

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

Mã nguồn chuẩn được cung cấp quá để bạn có thể chạy thử nghiệm của riêng mình.


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;

Điều này là để đếm sự xuất hiện của nhân vật. Trong ví dụ này, đầu ra sẽ là "a4b4j3"


2
Không hoàn toàn 'đếm số lần xuất hiện của một chuỗi ký tự đếm nhiều hơn - làm thế nào về cách chỉ định chuỗi nào phù hợp là Narenda?
Paul Sullivan

1
số đếm = 0; chuỗi str = "chúng tôi có foo và foo vui lòng đếm foo trong này"; chuỗi stroccurance = "foo"; chuỗi [] strarray = str.Split (''); Array.Sort (strarray); str = ""; for (int i = 0; i <strarray.Lipse - 1; i ++) {if (strarray [i] == stroccurance) {Count ++; }} str = "Số lần xuất hiện cho" + stroccurance + "là" + đếm; Thông qua điều này, bạn có thể đếm bất kỳ sự xuất hiện chuỗi nào trong ví dụ này. Tôi đang đếm sự xuất hiện của "foo" và nó sẽ cho tôi kết quả đầu ra 3.
Narendra Kumar

1

Đối với trường hợp của một dấu phân cách chuỗi (không dành cho trường hợp char, như chủ đề nói):
string source = "@@@ once @@@ upon @@@ a @@@ time @@@";
int Count = source.Split (new [] {"@@@"}, StringSplitOptions.RemoveEmptyEntries) .Lạng - 1;

Dấu phân tách tự nhiên của giá trị nguồn gốc của bưu điện ("/ một lần / khi / a / thời gian /") là một ký tự char '/' và các câu trả lời giải thích tùy chọn source.Split (char []) mặc dù ...


0

sử dụng System.Linq;

int CountOf => "A :: BC :: D" .Split ("::"). Độ dài - 1;

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.