Tiêu đề là câu hỏi. Dưới đây là nỗ lực của tôi để trả lời nó thông qua nghiên cứu. Nhưng tôi không tin tưởng vào nghiên cứu không được định dạng của mình nên tôi vẫn đặt ra câu hỏi (Cách nhanh nhất để lặp qua các ký tự riêng lẻ trong một chuỗi trong C# là gì?).Cách nhanh nhất để lặp qua các ký tự riêng lẻ trong một chuỗi trong C# là gì?
Thỉnh thoảng, tôi muốn quay vòng qua các ký tự của một chuỗi một, chẳng hạn như khi phân tích cú pháp cho các mã thông báo lồng nhau - một cái gì đó mà cannot be done with regular expressions. Tôi tự hỏi cách nhanh nhất là lặp qua các ký tự riêng lẻ trong một chuỗi, đặc biệt là các chuỗi rất lớn.
Tôi đã tự kiểm tra và kết quả của tôi dưới đây. Tuy nhiên có rất nhiều độc giả với kiến thức sâu hơn về trình biên dịch .NET CLR và C# vì vậy tôi không biết nếu tôi thiếu một cái gì đó hiển nhiên, hoặc nếu tôi đã mắc lỗi trong mã thử nghiệm của mình. Vì vậy, tôi thu hút phản ứng tập thể của bạn. Nếu bất cứ ai có cái nhìn sâu sắc vào cách trình chỉ mục chuỗi thực sự hoạt động sẽ rất hữu ích. (Có phải đó là một tính năng ngôn ngữ C# được biên dịch thành một cái gì đó khác đằng sau hậu trường không? Hoặc một cái gì đó được xây dựng trong CLR?).
Phương pháp đầu tiên sử dụng một dòng suối được lấy trực tiếp từ các câu trả lời chấp nhận từ các chủ đề: how to generate a stream from a string?
thử nghiệm
longString
là một chuỗi 99.100.000 nhân vật bao gồm 89 bản sao của phiên bản plain-text của đặc tả ngôn ngữ C#. Kết quả được hiển thị cho 20 lần lặp lại. Trường hợp có thời gian 'khởi động' (ví dụ như lần lặp đầu tiên của mảng được tạo ngầm trong phương thứC# 3), tôi đã thử nghiệm riêng biệt, chẳng hạn như bằng cách ngắt khỏi vòng lặp sau lần lặp đầu tiên.
Kết quả
Từ thử nghiệm của tôi, bộ nhớ đệm chuỗi trong một mảng char sử dụng phương pháp ToCharArray() được nhanh nhất cho iterating trên toàn bộ chuỗi. Phương thức ToCharArray() là một chi phí trả trước và quyền truy cập sau đó vào các ký tự riêng lẻ nhanh hơn một chút so với trình truy cập chỉ mục được tích hợp sẵn.
milliseconds
---------------------------------
Method Startup Iteration Total StdDev
------------------------------ ------- --------- ----- ------
1 index accessor 0 602 602 3
2 explicit convert ToCharArray 165 410 582 3
3 foreach (c in string.ToCharArray)168 455 623 3
4 StringReader 0 1150 1150 25
5 StreamWriter => Stream 405 1940 2345 20
6 GetBytes() => StreamReader 385 2065 2450 35
7 GetBytes() => BinaryReader 385 5465 5850 80
8 foreach (c in string) 0 960 960 4
Cập nhật: mỗi @ bình luận của Eric, đây là kết quả cho 100 lặp trên một bình thường hơn 1,1 M chuỗi char (một bản sao của C# spec). Indexer và mảng char vẫn còn nhanh nhất, tiếp theo là foreach (char trong chuỗi), tiếp theo là các phương thức stream.
milliseconds
---------------------------------
Method Startup Iteration Total StdDev
------------------------------ ------- --------- ----- ------
1 index accessor 0 6.6 6.6 0.11
2 explicit convert ToCharArray 2.4 5.0 7.4 0.30
3 for(c in string.ToCharArray) 2.4 4.7 7.1 0.33
4 StringReader 0 14.0 14.0 1.21
5 StreamWriter => Stream 5.3 21.8 27.1 0.46
6 GetBytes() => StreamReader 4.4 23.6 28.0 0.65
7 GetBytes() => BinaryReader 5.0 61.8 66.8 0.79
8 foreach (c in string) 0 10.3 10.3 0.11
Mã sử dụng (thử nghiệm riêng rẽ; hiển thị cùng cho ngắn gọn)
//1 index accessor
int strLength = longString.Length;
for (int i = 0; i < strLength; i++) { c = longString[i]; }
//2 explicit convert ToCharArray
int strLength = longString.Length;
char[] charArray = longString.ToCharArray();
for (int i = 0; i < strLength; i++) { c = charArray[i]; }
//3 for(c in string.ToCharArray)
foreach (char c in longString.ToCharArray()) { }
//4 use StringReader
int strLength = longString.Length;
StringReader sr = new StringReader(longString);
for (int i = 0; i < strLength; i++) { c = Convert.ToChar(sr.Read()); }
//5 StreamWriter => StreamReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(longString);
writer.Flush();
stream.Position = 0;
StreamReader str = new StreamReader(stream);
while (stream.Position < strLength) { c = Convert.ToChar(str.Read()); }
//6 GetBytes() => StreamReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(longString));
StreamReader str = new StreamReader(stream);
while (stream.Position < strLength) { c = Convert.ToChar(str.Read()); }
//7 GetBytes() => BinaryReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(longString));
BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
while (stream.Position < strLength) { c = br.ReadChar(); }
//8 foreach (c in string)
foreach (char c in longString) { }
câu trả lời chấp nhận:
Tôi giải thích @CodeInChaos và ghi chú Ben như sau:
fixed (char* pString = longString) {
char* pChar = pString;
for (int i = 0; i < strLength; i++) {
c = *pChar ;
pChar++;
}
}
Thực hiện 100 lần lặp lại trong ngắn hạn chuỗi là 4,4 ms, với < 0,1 ms st dev.
'ToCharArray()' tạo ra một ** bản sao mới ** của chuỗi "siêu dài", vì vậy ngay cả khi hiệu suất của nó đáng chú ý, tiêu thụ bộ nhớ vượt qua những lợi ích mà nó mang lại. – Tigran
Chỉ cần tự hỏi, đo lường trong LINQPad sử dụng 'System.Diagnostics.Stopwatch'? – kamranicus
Còn về 'foreach (char c in str)' thì sao? –