programing

문자열(실제로는 문자)의 발생을 어떻게 카운트합니까?

nicescript 2023. 4. 22. 22:53
반응형

문자열(실제로는 문자)의 발생을 어떻게 카운트합니까?

걸 있어요/ 수 , 그 후 가장) s 트 、 s 、 는 、 는 、 는 、 는 、 는 、 는는 、 는 、 만 만 、 만 만 、 만 、 만 만 만만 、 만 s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s s

지금 저는 다음과 같은 것을 하고 있습니다.

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

하지만 전 전혀 좋아하지 않아요, 누구 가져가는 사람 있나요?

않아RegEx걸위 、 내? 、 내??

내 끈이 내가 찾는 용어가 될거란걸 알아, 그러니 넌 추측할 수 있어...

물론 length가 1을 넘는 문자열의 경우,

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

를 사용하고 있는 경우.NET 3.5는 LINQ를 사용한 원라이너로 이 작업을 수행할 수 있습니다.

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

LINQ 를 사용하지 않는 경우는, 다음과 같이 실행할 수 있습니다.

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

당신의 독창적인 기술이 이것들 중 어느 것보다도 약 30% 더 빠른 것 같다는 것을 알게 되면 놀랄지도 모릅니다!방금 "/Once/on/a/time/"에 대한 간단한 벤치마크를 실시했는데, 결과는 다음과 같습니다.

= 12s = 12s
. Count= =19s
= [] = [17s]
포어치(bobwienholt의 답변에서) = 10s

(반복 횟수는 5천만 회이므로 실제 세계에서는 큰 차이를 느끼지 못할 것입니다.)

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

합니다.source.Replace()★★★★★★★★★★★★★★★★★★.

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

문자뿐만 아니라 문자열 전체를 검색할 수 있는 경우:

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

"문자열 내의 각 문자에 대해 해당 문자에서 시작하는 나머지 문자열을 하위 문자열로 가져옵니다. 대상 문자열로 시작하는 경우 해당 문자열을 세십시오."로 읽습니다.

가 몇 가지 조사를 해봤는데 리차드 왓슨의 해결책이 대부분의 경우 가장 빠르다는 을 발견했습니다.이 표는 투고된 모든 솔루션의 결과를 나타낸 표입니다('test {test'와 같은 문자열을 해석할 때 예외를 발생시키기 때문에 Regex를 사용하는 솔루션 제외).

    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는 모든 컬렉션에서 사용할 수 있으며 문자열은 단순한 문자 모음이기 때문에 이 멋진 한 줄짜리 라이너는 어떻습니까?

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

있어야 합니다.using System.Linq;코드 파일의 맨 위에 있습니다..Count는, 그 네임스페이스로부터의 확장 메서드입니다.

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

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

내 컴퓨터에서는 5000만 번 반복하는 모든 문자 솔루션보다 약 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++;
}

왜 그럴 수 있는지 궁금했고 구글에서 검색했더니(반복하는 것이 더 빠르다는 것을 기억한다) 짜증나게 문자열을 사용하여 char[] 기술을 이미 사용하고 있는 SO 질문을 하게 되었습니다.하지만 이런 맥락에서 역전은 새로운 것이라고 생각합니다.

C#의 문자열에서 각 문자를 반복하는 가장 빠른 방법은 무엇입니까?

둘 다 단일 문자 검색어에 대해서만 작동합니다.

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

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

바늘이 길면 더 나을지도 몰라

하지만 좀 더 우아한 방법이 있을 거야:)

편집:

source.Split('/').Length-1
Regex.Matches(input,  Regex.Escape("stringToMatch")).Count

C#에서는 String SubString 카운터가 의외로 까다로운 펠로우입니다.

public static int CCount(String haystack, String needle)
{
    return haystack.Split(new[] { needle }, StringSplitOptions.None).Length - 1;
}
private int CountWords(string text, string word) {
    int count = (text.Length - text.Replace(word, "").Length) / word.Length;
    return count;
}

원래 해결책은 chars가 가장 빨랐기 때문에 현악기도 마찬가지라고 생각합니다.그래서 저의 기여는 나의 기여입니다.

컨텍스트에 대해 다음과 같이 하십시오.저는 '그' 입니다 (스톤월 항쟁, 1969) 달 정도면 당분간은 정치상 충분한 것 같아

그레이, 벤

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

물론 그럴 거에요.Net5(Net 코어 2.1+& Net 표준 2.1)는 새로운 반복 속도 킹입니다.

"스페이트 <T>" https://learn.microsoft.com/en-us/dotnet/api/system.span-1?view=net-5.0

그리고 문자열은 스페인에 대한 내장되어 있습니다.캐릭터>

int count = 0;
foreach( var c in source.AsSpan())
{
    if (c == '/')
        count++;
}

검사 결과 직진 포어치보다 62% 빠릅니다또, 스팬상의 for()루프와 비교했습니다<T>[i], 및 여기에 투고된 다른 몇 개.String의 역방향 for() 반복은 스트레이트 포어치보다 느린 것 같습니다.

Starting test, 10000000 iterations
(base) foreach =   673 ms

fastest to slowest
foreach Span =   252 ms   62.6%
  Span [i--] =   282 ms   58.1%
  Span [i++] =   402 ms   40.3%
   for [i++] =   454 ms   32.5%
   for [i--] =   867 ms  -28.8%
     Replace =  1905 ms -183.1%
       Split =  2109 ms -213.4%
  Linq.Count =  3797 ms -464.2%

업데이트: 2021년 12월, Visual Studio 2022,NET 5 및 6

.NET 5
Starting test, 100000000 iterations set
(base) foreach =  7658 ms
fastest to slowest
  foreach Span =   3710 ms     51.6%
    Span [i--] =   3745 ms     51.1%
    Span [i++] =   3932 ms     48.7%
     for [i++] =   4593 ms     40.0%
     for [i--] =   7042 ms      8.0%
(base) foreach =   7658 ms      0.0%
       Replace =  18641 ms   -143.4%
         Split =  21469 ms   -180.3%
          Linq =  39726 ms   -418.8%
Regex Compiled = 128422 ms -1,577.0%
         Regex = 179603 ms -2,245.3%
         
         
.NET 6
Starting test, 100000000 iterations set
(base) foreach =  7343 ms
fastest to slowest
  foreach Span =   2918 ms     60.3%
     for [i++] =   2945 ms     59.9%
    Span [i++] =   3105 ms     57.7%
    Span [i--] =   5076 ms     30.9%
(base) foreach =   7343 ms      0.0%
     for [i--] =   8645 ms    -17.7%
       Replace =  18307 ms   -149.3%
         Split =  21440 ms   -192.0%
          Linq =  39354 ms   -435.9%
Regex Compiled = 114178 ms -1,454.9%
         Regex = 186493 ms -2,439.7%

루프를 추가하고 RegEx를 추가했습니다.그 때문에, 몇번이나 반복해 사용하는 것이 어떠한 재해인지 알 수 있습니다.for(+) 루프 비교는 에서 최적화되어 있을 가능성이 있다고 생각합니다.NET 6은 포어치 스팬과 거의 같은 속도이기 때문에 내부적으로 스팬을 사용합니다.

코드 링크

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

가장 쉬운 방법은 정규 표현을 사용하는 것이라고 생각합니다.이렇게 하면 myVar를 사용할 때와 동일한 분할 카운트를 얻을 수 있습니다.분할('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;

안전하지 않은 바이트 단위 비교와 같은 특정 하위 문자열 카운트가 부족하다고 느꼈습니다.오리지널 포스터의 방법이나 생각할 수 있는 방법을 정리했습니다.

이게 제가 만든 줄 확장자예요.

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는 Count SubstrX에 대응하고 CCX는 Count CharX에 대응합니다."longerword"는 문자열에서 "_", "and"는 문자열에서 "and", "mlw"는 문자열에서 "longerword"를 검색합니다.

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만 글자가 담긴 파일이 있었어요."derp adfderdserp dfaerpderp deasderp"가 100,000번 반복되었습니다.위의 방법으로 파일 내에서 "더프"를 검색하여 이 결과의 100배를 검색했습니다.

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

그래서 제 4번째 방법이 확실히 승자이지만, 현실적으로 360만 개의 문자 파일이 100번 동안 1586ms만 걸린다면, 이 모든 것은 무시해도 좋을 것입니다.

덧붙여서, Count Substr과 Count Char 메서드의 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자 바늘에 매우 좋지 않습니다.

주의: 모든 값이 릴리스 버전 출력으로 업데이트되었습니다.처음 게시했을 때 릴리스 모드로 빌드하는 것을 깜빡했습니다.제 진술 중 일부는 수정되었습니다.

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

이것은 프로그램이 "/s"를 정확하게 찾을 때마다 카운트되며(대문자와 소문자가 구분됨), 발생 횟수가 "발생" 변수에 저장됩니다.

문자열 발생에 대한 일반 함수:

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

문자열에서 조건문을 테스트하는 것과 유사한 작업을 수행해야 합니다.

찾고 있던 것을 1개의 문자로 대체하고, 1개의 문자의 인스턴스를 세어 보았습니다.

잘못된 카운트를 피하기 위해 스트링에 존재하지 않는 문자를 체크해야 합니다.

문자열:

"..JD JD JD 등"에서 "etc"를 찾습니다.JDJDJDJDJDJD 등"

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.

이 파일을 불량/불량 상태로 폐기하기 전에 성능을 확인하십시오.

첫 번째 견해는 다음과 같습니다.

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

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

치환과 분할을 사용하는 건초 더미 접근방식의 바늘은 21초 이상 걸리는 반면, 여기에는 약 15.2초가 소요됩니다.

한 후 하여 ""를 추가합니다.substring.Length - 1입니다.charIndex(그래서)는 116초입니다.

편집 2: 26개의 2문자열이 있는 문자열을 사용했습니다.다음은 같은 샘플 텍스트로 갱신된 시간입니다.

건초더미에서 바늘 찾기(OP 버전): 7.8초

권장 메커니즘: 4.6초

편집 3: 한 글자 모서리 대소문자를 더하면 1.2초가 됩니다.

편집 4: 컨텍스트: 5000만 번의 반복이 사용되었습니다.

링에 확장 메서드를 삽입하려고 했습니다(자세한 것은 코멘트 참조).공식적인 벤치 마킹은 하지 않았지만, 대부분의 시나리오에서는 매우 빠를 필요가 있다고 생각합니다.

편집: OK - 이 질문으로 현재 구현의 퍼포먼스가 여기에 제시된 솔루션 중 몇 가지와 어떻게 비교될지 궁금하게 되었습니다.벤치 마킹을 조금 해보기로 했는데, 큰 문자열(100Kb +), 큰 기판(32Kb +), 많은 삽입 반복(10K +)을 사용하여 적극적으로 검색하기 전까지는 Richard Watson이 제공한 솔루션의 성능과 거의 일치한다는 것을 알게 되었습니다.그 시점에서는 솔루션의 속도가 약 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;
    }

SplitIndexOf(미국의)

위의 벤치마크는 잘못된 문자열이 Richard Watson이 가장 빠르다는 것을 나타내고 있는 것 같습니다(아마도 테스트 데이터에서 차이가 날 수 있지만, 아래 이유로 인해 이상하게 느껴집니다).

에 기재되어 있는 이러한 방법의 실장에 대해 조금 더 자세히 설명하겠습니다.NET(Luke H, Richard Watson 메서드의 경우),

  • IndexOf 문화에 따라 Read Only Span을 검색/작성하고 대소문자를 무시해야 하는지 등을 확인합니다.마지막으로 안전하지 않은/원어민 통화를 합니다.
  • Split 는 여러 개의 구분자를 처리할 수 있으며 StringSplitOptions도 있습니다.또한 string[] 배열을 생성하여 분할 결과로 채워야 합니다(서브스트링도 일부 있습니다).문자열 발생 수에 따라 분할이 IndexOf보다 빠를 수 있습니다.

덧붙여서 저는 Index Of(포인터를 사용하면 더 빠를 수 있지만 안전하지 않을 수 있지만 대부분의 경우 체크되지 않은 상태)를 최소 4배 빠르게 만들었습니다.

벤치마크(GitHub 소스)

셰익스피어 리처드 3세의 일반적인 단어(the) 또는 작은 문장 중 하나를 검색하여 수행합니다.

방법 의미하다 에러 표준 개발 비율
리처드_롱인롱 67.721 us 1.0278 us 0.9614 us 1.00
루크_롱인롱 1.960 us 0.0381 us 0.0637 us 0.03
Fab_LongInLong 1. 델에게 문의하다 0.0160 us 0.0142 us 0.02
-------------------- -----------: ----------: ----------: ------:
리처드_쇼트인롱 104.771 us 2.8117 us 7.9304 델 1.00
Luke_ShortInLong 2.971 us 0.0594us 0.0813 us 0.03
Fab_ShortInLong 2. 델에게 문의하다 0.0419 us 0.0411 us 0.02
--------------------- ----------: ---------: ---------: ------:
Richard_ShortInShort 115.53 ns 1.359 ns 1.135 ns 1.00
Luke_ShortInShort 52.46 ns 0.970 ns 0.908 ns 0.45
Fab_ShortInShort 28.47 ns 0.552 ns 0.542 ns 0.25
public int GetOccurrences(string input, string needle)
{
    int count = 0;
    unchecked
    {
        if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(needle))
        {
            return 0;
        }

        for (var i = 0; i < input.Length - needle.Length + 1; i++)
        {
            var c = input[i];
            if (c == needle[0])
            {
                for (var index = 0; index < needle.Length; index++)
                {
                    c = input[i + index];
                    var n = needle[index];

                    if (c != n)
                    {
                        break;
                    }
                    else if (index == needle.Length - 1)
                    {
                        count++;
                    }
                }
            }
        }
    }

    return count;
}

.NET 7에서는 할당이 필요 없는(및 고도로 최적화된) Regex API를 사용하고 있습니다.계산은 특히 쉽고 효율적입니다.

    var input = "abcd abcabc ababc";
    var result = Regex.Count(input: input, pattern: "abc"); // 4

다이내믹 패턴을 조합할 때는, 이스케이프 해 주세요.

public static int CountOccurences(string input, string pattern)
{
    pattern = Regex.Escape(pattern); // Aww, no way to avoid heap allocations here

    var result = Regex.Count(input: input, pattern: pattern);
    return result;
}

그리고 고정 패턴에 대한 보너스로, 를 사용합니다.NET 7은 정규식 문자열을 소스 생성 코드로 변환하는 데 도움이 되는 분석기를 도입했습니다.이는 regex의 런타임 컴파일 오버헤드를 피할 수 있을 뿐만 아니라 구현 방법을 보여주는 매우 읽기 쉬운 코드를 제공합니다.사실, 이 코드는 적어도 수동으로 작성하는 대체 코드만큼 효율적입니다.

regex 콜이 적합한 경우 분석기는 힌트를 제공합니다."Convert to 'GeneratedRegexAttribute'를 선택하면 다음과 같은 결과를 얻을 수 있습니다.

[GeneratedRegex("abc")]
private static partial Regex MyRegex(); // Go To Definition to see the generated code
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");

웹 페이지를 보면 병렬 루프를 포함한 15가지 다른 방법이 벤치마킹되어 있습니다.

가장 빠른 방법은 단일 스레드 for-loop 중 하나를 사용하는 것입니다(있는 경우).넷 버전 < 4.0) 또는 병행.for loop (사용하는 경우)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.
        }
    }
}

벤치마크 소스 코드도 제공되므로 자체 테스트를 실행할 수 있습니다.

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" 출력이 됩니다.

언급URL : https://stackoverflow.com/questions/541954/how-would-you-count-occurrences-of-a-string-actually-a-char-within-a-string

반응형