문자열 내에서 문자열 (실제로는 char)의 발생을 어떻게 계산합니까?


864

나는 /문자열에서 찾을 수있는 수 를 세고 싶다는 것을 깨달은 것을하고 있는데, 여러 가지 방법이 있었지만 가장 좋았거나 가장 쉬운 것이 무엇인지 결정할 수 없었습니다. .

현재 나는 다음과 같은 일을하고있다 :

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

그러나 나는 그것을 전혀 좋아하지 않는다.

나는 이것을 정말로 파고 싶지 않다 RegEx.

내 문자열에 내가 찾고있는 용어가 있다는 것을 알고 있으므로 다음과 같이 가정 할 수 있습니다 ...

물론 길이> 1 인 문자열의 경우

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

34
+1 : 매우 다른 방식으로 계산해야합니다. 나는 벤치 마크 테스트 결과에 놀랐다 :)
naveen

4
그리 다르지 않습니다 ... SQL 에서이 기능을 구현하는 일반적인 방법 LEN(ColumnToCheck) - LEN(REPLACE(ColumnToCheck,"N",""))입니다.
Sheridan

6
실제로는 "/"로 나누어야합니다. 길이
Gerard

3
"/////"내에서 "//"발생 횟수를 계산해야한다는 요구 사항은 무엇입니까? 2 또는 4?

1
아마 정규식을 사용하는 것이 가장 좋은 방법 일 것입니다.
Adam Higgins

답변:


1009

.NET 3.5를 사용하는 경우 LINQ의 단일 라이너 에서이 작업을 수행 할 수 있습니다.

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

LINQ를 사용하지 않으려면 다음을 수행하십시오.

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

원래 기술이이 기술들 중 약 30 % 더 빠르다는 것을 알게되면 놀랄 것입니다! 방금 "/ once / upon / a / time /"으로 빠른 벤치 마크를 수행했으며 결과는 다음과 같습니다.

원본 = 12

소스 카운트 = 19 초 소스 스플릿 = 17 초
foreach ( bobwienholt의 답변에서 ) = 10 초

(시간은 50,000,000 회 반복되므로 실제 세계에서 큰 차이를 느끼지 못할 것입니다.)


6
예, VS는 문자열 클래스에서 LINQ 확장 메서드를 숨 깁니다. 그들은 개발자가 모든 확장 메소드가 문자열 클래스에 표시되는 것을 원하지 않을 것이라고 생각합니다. 아마 현명한 결정일 것입니다.
유다 가브리엘 히 망고

11
이 동작은 VS2010이 새 클래스 파일에 System.Linq를 자동으로 포함하기 때문에 가능하며 VS2008은 그렇지 않습니다. 인텔리전스가 작동하려면 네임 스페이스가 있어야합니다.
Sprague

30
개수 및 분할 솔루션은 문자를 계산할 때만 작동합니다. OP 솔루션과 마찬가지로 문자열에는 작동하지 않습니다.
Peter Lillevold

5
f == '\' 문자열의 문자열이 아니라 문자열의 문자에 관한 것입니다
Thomas Weller

9
이것은 다른 질문에 대한 대답처럼 보입니다 : "문자열 내에서 문자의 발생 횟수를 어떻게 계산 하시겠습니까?"
벤 애런 슨

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

source.Replace()자체 보다 빨라야 합니다.


18
foreach 대신 for로 전환하면 아주 작은 비트만으로 조금만 개선 할 수 있습니다.
Mark

17
아니오. 질문은 문자가 아닌 문자열의 발생을 세도록 요청합니다.
YukiSakura

3
문자열의 문자를 세고 있습니다. 제목은 문자열에서 문자열을 세는 것에 관한 것입니다
Thomas Weller

2
@Mark 방금 for 루프로 테스트했으며 실제로 foreach를 사용하는 것보다 느 렸습니다. 경계 검사 때문일 수 있습니까? (5 mil 반복에서 2.05 대 2.05의 시간이 1.65 초였습니다.)
측정

4
질문이 문자열 내에서 문자열을 요구하는 동안 OP 게시 된 문제 예는 실제로 한 문자입니다.이 경우 더 나은 방법을 보여 주므로이 답변을 여전히 유효한 솔루션이라고 부릅니다 (문자열 검색 대신 문자 검색) 당면한 문제를 해결하기 위해.
차드

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

8
+ 1— 경우에 따라 추가하고 싶을 수도 있습니다 RegexOptions.IgnoreCase.
TrueWill

3
엄청나게 낮지 않습니까?
Thomas Ayoub

4
정규식 오버 헤드는 이상적이지 않으며 "정말 정규식을 파고 싶지 않습니까?"
차드

하지 않을 수 있습니다 Regex.Escape(...)그래서new System.Text.RegularExpressions.Regex(needle).Matches(haystack).Count;
barlop

2
나는 문자뿐만 아니라 문자열을 검색 할 수 있기 때문에 이것과 함께 갔다.
James in Indy

86

문자뿐만 아니라 전체 문자열을 검색하려면 다음을 수행하십시오.

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

"문자열의 각 문자에 대해 해당 문자에서 시작하는 나머지 문자열을 하위 문자열로 사용하고 대상 문자열로 시작하는 경우 계산하십시오."


1
주어진 설명보다 명확한 방법으로 설명 할 수 있는지 잘 모르겠습니다. 혼란스러운 것은 무엇입니까?
mqp

58
슈퍼 슬로우! html 페이지에서 시도했지만 2 초가 걸리는이 페이지의 다른 방법과 비교하여 약 2 분이 걸렸습니다. 대답은 정확했다. 사용하기에는 너무 느 렸습니다.
JohnB

2
너무 느리게 동의했다. 나는 linq 스타일 솔루션의 큰 팬이지만 이것은 실용적이지 않습니다.
Sprague

5
이것이 느린 이유는 n 개의 문자열을 생성하여 대략 n ^ 2 / 2 바이트를 할당하기 때문입니다.
Peter Crabtree

6
210000 자 문자열에 OutOfMemoryException이 발생합니다.
ender

66

조사한 결과 , 대부분 Richard Richard의 솔루션이 가장 빠릅니다. 그것은 게시물에있는 모든 솔루션의 결과가있는 표입니다 ( "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;
    }
}

하위 문자열을 만들거나 바꾸기 / 분할 또는 Regex / Linq를 사용하지 않고 자체 '낮은 수준의'솔루션을 추가하려고했지만 내 것보다 나을 수도 있습니다 (적어도 더 짧습니다). 감사!
Dan W

Regex 솔루션의 경우Regex.Escape(needle)
Thymine

2
다른 사람들을 지적하기 위해 비어 있으면 검색 값을 확인해야합니다. 그렇지 않으면 무한 루프에 빠지게됩니다.
WhoIsRich

2
어쩌면 그냥 날,하지만 위해 source="aaa" substring="aa"나는이 변경되지 1. "수정"에,이 돌아갈 것으로 예상 n += substring.Lengthn++
ytoledano

다음 overlapped과 같이 사례를 충족시키기 위해 플래그를 추가 할 수 있습니다 .overlapped=True;.... if(overlapped) {++n;} else {n += substring.Length;}
tsionyx

54

LINQ는 모든 컬렉션에서 작동하며 문자열은 문자의 모음이므로이 멋진 한 줄짜리는 어떻습니까?

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

되어 있는지 확인하십시오 using System.Linq;으로, 코드 파일의 맨 위에 .Count해당 네임 스페이스에서 확장 방법이다.


5
var를 사용할 가치가 있습니까? Count가 int를 반환하지 않는 것으로 대체 될 가능성이 있습니까?
Whatsit

69
@Whatsit : 왼손으로 'var'을 입력 할 수 있으며 'int'는 양손이 필요합니다.)
Sean Bright

7
int문자는 모두 홈 키에 있지만 var그렇지는 않습니다. 어 .. 잠깐만, 저는 Dvorak을 사용하고 있습니다
Michael Buen

2
@BDotA 'using System.Linq;'이 있는지 확인하십시오. 파일 상단에. 또한 intellisense는 문자열이기 때문에 .Count 호출을 숨길 수 있습니다. 그럼에도 불구하고 컴파일되고 잘 실행될 것입니다.
유다 가브리엘 히 망고

3
@JudahGabrielHimango 나는 변수형 이 명백하고 (간결하고 일관성있게) var가 특히 사용되어야한다고 주장 할 것이다
EriF89

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

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

내 컴퓨터에서는 5 천만 번 반복되는 모든 문자 솔루션보다 약 2 초 빠릅니다.

2013 년 개정 :

문자열을 char []로 변경하고 반복하십시오. 50m 반복 동안 총 시간을 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 #에서 문자열의 개별 문자를 반복하는 가장 빠른 방법은 무엇입니까?


1
while의 대괄호를 source.IndexOf('/', n + 1)잃어 버릴 수 있습니다 n++:) 또한 string word = "/"문자 대신 변수 를 넣으십시오 .
neeK12

1
Niko, 새로운 답변을 확인하십시오. 그러나 가변 길이 부분 문자열을 만들기가 더 어려울 수 있습니다.
Richard Watson

subtring을 단계별로 실행하여 비슷한 것을 사용했습니다. indexOf에 startIndex가 있음을 깨달을 때까지입니다. 속도와 메모리 사용 공간 사이의 균형이 좋기 때문에 첫 번째 솔루션이 가장 좋습니다.
Samir Banjanovic

1
값을 0과 비교하는 것이 더 빠르기 때문에 뒤로 반복하는 것이 더 빠릅니다.
reggaeguitar

1
트윗 담아 가기 기본 코드를 보면 기본 호출입니다. public char [] toCharArray () {... System.arraycopy (값, 0, 결과, 0, 값. 길이); ...}
Richard Watson

46

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

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

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

더 긴 바늘에 더 나은 것으로 판명 될 수 있습니다 ...

그러나 더 우아한 방법이 있어야합니다. :)


복수 문자 교체를 설명합니다. 그것없이, "테스트는 열쇠"에서 "the"를
세면

이것을 벤치마킹 및 스트링과 비교 .Split-way-약 1.5 배 빠르게 작동합니다. 명성.
Alex

20

편집하다:

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

2
이것이 제가하는 것입니다. 그리고 source.Split(new[]{"//"}, StringSplitOptions.None).Count - 1여러 문자 구분 기호.
bzlm

4
이것은 힙에서 적어도 n 개의 문자열 할당을 수행하고 (어쩌면) 몇 가지 배열 크기 조정을 수행합니다. 매우 비효율적이며 확장 성이 떨어지며 중요한 코드에 사용해서는 안됩니다.
Zar Shardan

17

C #에서 멋진 String SubString 카운터는 예상치 못한 까다로운 동료입니다.

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

1
좋은 해결책-문자열뿐만 아니라 문자 작업도 가능합니다!
ChriPf

고맙게도 언어를 바꿀 때 문자열 처리의 미묘한 부분을 잊어 버리는 것은 너무 쉽습니다. 오늘날 대부분의 사람들과 마찬가지로!
Dave

1
-1 이유 : Count ()와 Count 또는 Length의 차이점을 알고 있습니까? 누군가 Count 또는 Length 대신 Count ()를 사용하면 트리거됩니다. Count ()는 IEnumerator를 생성 한 다음 IEnumerable의 모든 발생을 통해 진행되는 반면 Count 또는 Length는 이미 모든 요소를 ​​반복 할 필요없이 원하는 수를 이미 보유하고있는 개체의 속성을 설정합니다.
aeroson

좋은 점은 이상한 점은 라이브러리에서 함수를 사용한 위치에서 "길이"를 사용한다는 것입니다. 편집했습니다!
Dave


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

원래 솔루션은 문자에 가장 빠르기 때문에 문자열에도 적합하다고 생각합니다. 여기 내 공헌이 있습니다.

상황 : 로그 파일에서 '실패'및 '성공'과 같은 단어를 찾고있었습니다.

Gr, 벤


2
"word"변수에 빈 문자열을 전달하지 마십시오 (0으로 나누기).
앤드류 젠스

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

20
또는 Regex.Matches (s, "65") Count ^ _ ^
Meta

모든 문자열에 대해 작동하지는 않습니다. "abc ++ def ++ xyz"에서 "++"를 검색해보십시오
marsh-wiggle

7

String 확장 방법을 사용할 준비가 된 사람이라면

여기 내가 게시 한 답변 중 가장 좋은 것을 기반으로 사용하는 것이 있습니다.

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

        int count    = 0;
        int position = 0;

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

        return count;
    }

    /// <summary> Returns the number of occurences of a single character within a string. </summary>
    public static int Occurrences(this System.String input, char value)
    {
        int count = 0;
        foreach (char c in input) if (c == value) count += 1;
        return count;
    }
}

전달 된 문자열이 null이거나 비어 있으면 두 번째 방법이 붐을 일으키지 않습니까? 순수하게 스타일 관점에서 입력을 문자열이 아닌 System.String으로 무엇을 정의하고 있습니까?
Nodoid

7
public static int GetNumSubstringOccurrences(string text, string search)
{
    int num = 0;
    int pos = 0;

    if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(search))
    {
        while ((pos = text.IndexOf(search, pos)) > -1)
        {
            num ++;
            pos += search.Length;
        }
    }
    return num;
}

5

가장 쉬운 방법은 정규 표현식을 사용하는 것입니다. 이 방법으로 myVar.Split ( 'x')를 사용할 수 있지만 다중 문자 설정에서 사용할 수있는 것과 동일한 분할 수를 얻을 수 있습니다.

string myVar = "do this to count the number of words in my wording so that I can word it up!";
int count = Regex.Split(myVar, "word").Length;

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

이것은 프로그램이 "/ s"를 정확하게 (대소 문자 구분) 발견 할 때마다 계산되며이 발생 횟수는 변수 "발생"에 저장됩니다.


3

안전하지 않은 바이트 별 비교와 같은 특정 종류의 하위 문자열 계산이 부족하다고 느꼈습니다. 나는 원래의 포스터 방법과 내가 생각할 수있는 방법을 모았다.

이것들은 내가 만든 문자열 확장입니다.

namespace Example
{
    using System;
    using System.Text;

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

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

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

            return count;
        }

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

            return count;
        }

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

            return count;
        }

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

            return count;
        }

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

            return count;
        }

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

            return count;
        }

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

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

            return count;
        }

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

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

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

            return count;
        }

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

테스트 코드가 뒤 따릅니다.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

결과 : CSX는 CountSubstrX에 해당하고 CCX는 CountCharX에 해당합니다. "chr"은 문자열에서 '_'를 검색하고 "and"는 문자열에서 "and"를 검색하고 "mlw"는 "muchlongerword"를 검색합니다.

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

그리고 마지막으로 360 만 문자의 파일이있었습니다. "derp adfderdserp dfaerpderp deasderp"는 100,000 번 반복되었습니다. 위의 메소드를 사용하여 파일 내에서 "derp"를 100 번 검색했습니다.

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

따라서 제 4 번째 방법은 확실히 승자이지만 실제로는 360 만 개의 문자 파일을 100 배나 더 나쁜 경우로 1586ms 만 걸린다면이 모든 것이 무시할 수 있습니다.

그건 그렇고, 100 번 CountSubstr 및 CountChar 메소드를 사용하여 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

이것에 따르면 원래의 포스터 방법은 큰 건초 더미에서 단일 문자 바늘에 매우 나쁩니다.

참고 : 모든 값은 릴리스 버전 출력으로 업데이트되었습니다. 처음 게시했을 때 실수로 릴리스 모드를 구축하는 것을 잊었습니다. 내 진술 중 일부가 수정되었습니다.


성능 결과에 감사드립니다. 속도 10의 요인 차이는 linq 또는 다른 깔끔하게 작성된 솔루션을 고려하지 않고 확장 방법을 사용해야하는 이유 일 수 있습니다.
Andreas Reiff

2

문자열 발생을위한 일반적인 함수 :

public int getNumberOfOccurencies(String inputString, String checkString)
{
    if (checkString.Length > inputString.Length || checkString.Equals("")) { return 0; }
    int lengthDifference = inputString.Length - checkString.Length;
    int occurencies = 0;
    for (int i = 0; i < lengthDifference; i++) {
        if (inputString.Substring(i, checkString.Length).Equals(checkString)) { occurencies++; i += checkString.Length - 1; } }
    return occurencies;
}

2
이것은 많은 수의 임시 문자열을 생성하고 가비지 수집기의 작동을 매우 어렵게 만듭니다.
EricLaw

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

Richard Watson의 답변에 대한 변형으로 문자열에서 문자열이 더 많이 발생하고 코드가 적을수록 효율성이 향상되어 약간 빨라졌습니다!

모든 시나리오를 광범위하게 테스트하지 않고 다음과 같이 사용하면 속도가 크게 향상되었습니다.

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

2
            var conditionalStatement = conditionSetting.Value;

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

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

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

문자열에서 조건문을 테스트하는 것과 비슷한 작업이 필요했습니다.

내가 찾던 것을 단일 문자로 바꾸고 단일 문자의 인스턴스를 세었습니다.

분명히 잘못된 카운트를 피하기 위해 사용중인 단일 문자가 문자열에 존재하지 않는지 확인해야합니다.


2

문자열의 문자열 :

".. JD JD JD JD 등 및 JDJDJDJDJDJDJDJD 등"에서 "etc"를 찾으십시오.

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

소리가 나지 않고 서투른 것으로 폐기하기 전에 성능을 점검하십시오.


2

내 첫 테이크는 나에게 다음과 같은 것을 주었다.

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

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

교체 및 나누기를 사용하는 건초 더미 방식의 바늘은 21+ 초를 생성하는 데 약 15.2 초가 걸립니다.

추가 할 비트를 추가 한 후 편집 substring.Length - 1charIndex에 할 해야합니다 (예 : 11.6 초).

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

건초 더미의 바늘 (OP 버전) : 7.8 초

권장 메커니즘 : 4.6 초

편집 3 : 단일 문자 코너 케이스를 추가하면 1.2 초가되었습니다.

편집 4 : 컨텍스트 : 5 천만 회 반복이 사용되었습니다.


2

확장 방법을 링에 넣을 것이라고 생각했습니다 (자세한 내용은 주석 참조). 나는 공식적인 벤치 마킹을하지 않았지만 대부분의 시나리오에서 매우 빠르다고 생각합니다.

편집 : 확인-그래서이 SO 질문은 현재 구현의 성능이 여기에 제시된 일부 솔루션과 어떻게 비교되는지 궁금해졌습니다. 나는 약간의 벤치 마킹을하기로 결정했고 우리 솔루션이 Richard Watson이 제공 한 솔루션의 성능과 매우 일치한다는 것을 알았습니다. 이 큰 문자열 (100 이하 +) 큰 문자열 (32 이하 +와 공격적인 검색을 수행 할 때까지 최대 ) 및 많은 포함 된 반복 (10K +) 이 시점에서 우리 솔루션은 약 2 배에서 4 배 정도 느 렸습니다. 이것이 우리가 Richard Watson이 제시 한 솔루션을 정말로 좋아한다는 사실을 감안하여 우리는 그에 따라 솔루션을 리팩토링했습니다. 나는이 혜택을 누릴 수있는 사람이라면 누구나 사용할 수있게 만들고 싶었습니다.

우리의 독창적 인 솔루션 :

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

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

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

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

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

        return count;
    }

그리고 여기 우리의 수정 된 솔루션이 있습니다 :

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

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

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

        return count;
    }

1
string Name = "Very good nice one is very good but is very good nice one this is called the term";
bool valid=true;
int count = 0;
int k=0;
int m = 0;
while (valid)
{
    k = Name.Substring(m,Name.Length-m).IndexOf("good");
    if (k != -1)
    {
        count++;
        m = m + k + 4;
    }
    else
        valid = false;
}
Console.WriteLine(count + " Times accures");

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

문자열의 모든 문자를 확인하기 만하면 해당 문자가 검색중인 문자이면 계산할 문자를 추가하십시오.


1

이 웹 페이지체크 아웃하면 병렬 루프 사용을 포함하여이 작업을 수행하는 15 가지 방법이 벤치마킹됩니다.

가장 빠른 방법은 단일 스레드 for-loop (.Net 버전 <4.0 인 경우) 또는 parallel.for 루프 (수천 개의 검사로 .Net> 4.0을 사용하는 경우)를 사용하는 것으로 보입니다.

"ss"가 검색 문자열이라고 가정하고, "ch"는 문자 배열 (찾고있는 문자가 둘 이상인 경우)입니다. 단일 스레드가 가장 빠른 런타임 코드의 기본 요지는 다음과 같습니다.

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

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


1
str="aaabbbbjjja";
int count = 0;
int size = str.Length;

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

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

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

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

캐릭터 발생을 계산하기위한 것입니다. 이 예제의 출력은 "a4b4j3"입니다.


2
'문자열 발생 횟수 계산'이 아닌 문자 수 계산-Narenda가 일치하는 문자열을 지정하는 방법은 어떻습니까?
Paul Sullivan

1
정수 카운트 = 0; string str = "우리는 foo와 foo를 가지고 있습니다. 이것에 foo를 세십시오"; 문자열 stroccurance = "foo"; 문자열 [] strarray = str.Split ( ''); Array.Sort (스트레이); str = ""; for (int i = 0; i <strarray.Length-1; i ++) {if (strarray [i] == stroccurance) {count ++; }} str = ""+ stroccurance + "에 대한 발생 횟수는"+ count; 이를 통해이 예제에서 문자열 발생을 계산할 수 있습니다. "foo"의 발생을 계산하고 출력 3을 줄 것입니다.
Narendra Kumar

1

문자열 구분 기호의 경우 (피험자가 말한대로 문자 경우가 아님) :
string source = "@@@ once @@@ upon @@@ a @@@ time @@@";
int count = source.Split (new [] { "@@@"}, StringSplitOptions.RemoveEmptyEntries) .Length-1;

포스터의 원래 소스 값 ( "/ once / upon / a / time /") 자연 구분 기호는 문자 '/'이며 응답은 source.Split (char []) 옵션을 설명합니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.