문자열(실제로는 문자)의 발생을 어떻게 카운트합니까?
걸 있어요/
수 , 그 후 가장) 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;
}
Split
IndexOf
(미국의)
위의 벤치마크는 잘못된 문자열이 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
'programing' 카테고리의 다른 글
bash에서 alt + numeric을 누르면 (arg [numeric])이 표시됩니다. (0) | 2023.04.22 |
---|---|
두 Excel 워크시트의 차이점을 찾으십니까? (0) | 2023.04.22 |
문자열을 제목 대소문자로 변환 (0) | 2023.04.22 |
토글 버튼 그룹을 WPF의 라디오 버튼처럼 동작시키는 방법 (0) | 2023.04.22 |
EPPlus를 사용하여 INT를 반환하는 Excel 날짜 열 (0) | 2023.04.22 |