我只需要在C#2.0中编写一个字符串反向函数(即LINQ不可用)并想出了这个:
public string Reverse(string text) { char[] cArray = text.ToCharArray(); string reverse = String.Empty; for (int i = cArray.Length - 1; i > -1; i--) { reverse += cArray[i]; } return reverse; }
就个人而言,我并不是对这个功能感到疯狂,并且我确信有更好的方法可以做到这一点.在那儿?
public static string Reverse( string s ) { char[] charArray = s.ToCharArray(); Array.Reverse( charArray ); return new string( charArray ); }
在这里,妥善反转字符串的解决方案"Les Mise\u0301rables"
的"selbare\u0301siM seL"
.这应该呈现selbarésiM seL
,不是selbar?esiM seL
(注意重音的位置),大多数实现的结果都是基于代码单元(Array.Reverse
等)或甚至代码点(特别注意代理对的反转).
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; public static class Test { private static IEnumerableGraphemeClusters(this string s) { var enumerator = StringInfo.GetTextElementEnumerator(s); while(enumerator.MoveNext()) { yield return (string)enumerator.Current; } } private static string ReverseGraphemeClusters(this string s) { return string.Join("", s.GraphemeClusters().Reverse().ToArray()); } public static void Main() { var s = "Les Mise\u0301rables"; var r = s.ReverseGraphemeClusters(); Console.WriteLine(r); } }
(现场运行示例:https://ideone.com/DqAeMJ)
它只是使用.NET API进行字形集群迭代,它从那时起一直存在,但看起来有点"隐藏".
这结果是一个令人惊讶的棘手问题.
我建议在大多数情况下使用Array.Reverse,因为它是本地编码的,维护和理解非常简单.
在我测试的所有情况下,它似乎都优于StringBuilder.
public string Reverse(string text) { if (text == null) return null; // this was posted by petebob as well char[] array = text.ToCharArray(); Array.Reverse(array); return new String(array); }
对于使用Xor的某些字符串长度,有第二种方法可以更快.
public static string ReverseXor(string s) { if (s == null) return null; char[] charArray = s.ToCharArray(); int len = s.Length - 1; for (int i = 0; i < len; i++, len--) { charArray[i] ^= charArray[len]; charArray[len] ^= charArray[i]; charArray[i] ^= charArray[len]; } return new string(charArray); }
注意如果要支持完整的Unicode UTF16字符集,请阅读此内容.而是使用那里的实现.它可以通过使用上述算法之一进行进一步优化,并在字符串反转后通过字符串进行清理.
这是StringBuilder,Array.Reverse和Xor方法之间的性能比较.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace ConsoleApplication4 { class Program { delegate string StringDelegate(string s); static void Benchmark(string description, StringDelegate d, int times, string text) { Stopwatch sw = new Stopwatch(); sw.Start(); for (int j = 0; j < times; j++) { d(text); } sw.Stop(); Console.WriteLine("{0} Ticks {1} : called {2} times.", sw.ElapsedTicks, description, times); } public static string ReverseXor(string s) { char[] charArray = s.ToCharArray(); int len = s.Length - 1; for (int i = 0; i < len; i++, len--) { charArray[i] ^= charArray[len]; charArray[len] ^= charArray[i]; charArray[i] ^= charArray[len]; } return new string(charArray); } public static string ReverseSB(string text) { StringBuilder builder = new StringBuilder(text.Length); for (int i = text.Length - 1; i >= 0; i--) { builder.Append(text[i]); } return builder.ToString(); } public static string ReverseArray(string text) { char[] array = text.ToCharArray(); Array.Reverse(array); return (new string(array)); } public static string StringOfLength(int length) { Random random = new Random(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { sb.Append(Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)))); } return sb.ToString(); } static void Main(string[] args) { int[] lengths = new int[] {1,10,15,25,50,75,100,1000,100000}; foreach (int l in lengths) { int iterations = 10000; string text = StringOfLength(l); Benchmark(String.Format("String Builder (Length: {0})", l), ReverseSB, iterations, text); Benchmark(String.Format("Array.Reverse (Length: {0})", l), ReverseArray, iterations, text); Benchmark(String.Format("Xor (Length: {0})", l), ReverseXor, iterations, text); Console.WriteLine(); } Console.Read(); } } }
结果如下:
26251 Ticks String Builder (Length: 1) : called 10000 times. 33373 Ticks Array.Reverse (Length: 1) : called 10000 times. 20162 Ticks Xor (Length: 1) : called 10000 times. 51321 Ticks String Builder (Length: 10) : called 10000 times. 37105 Ticks Array.Reverse (Length: 10) : called 10000 times. 23974 Ticks Xor (Length: 10) : called 10000 times. 66570 Ticks String Builder (Length: 15) : called 10000 times. 26027 Ticks Array.Reverse (Length: 15) : called 10000 times. 24017 Ticks Xor (Length: 15) : called 10000 times. 101609 Ticks String Builder (Length: 25) : called 10000 times. 28472 Ticks Array.Reverse (Length: 25) : called 10000 times. 35355 Ticks Xor (Length: 25) : called 10000 times. 161601 Ticks String Builder (Length: 50) : called 10000 times. 35839 Ticks Array.Reverse (Length: 50) : called 10000 times. 51185 Ticks Xor (Length: 50) : called 10000 times. 230898 Ticks String Builder (Length: 75) : called 10000 times. 40628 Ticks Array.Reverse (Length: 75) : called 10000 times. 78906 Ticks Xor (Length: 75) : called 10000 times. 312017 Ticks String Builder (Length: 100) : called 10000 times. 52225 Ticks Array.Reverse (Length: 100) : called 10000 times. 110195 Ticks Xor (Length: 100) : called 10000 times. 2970691 Ticks String Builder (Length: 1000) : called 10000 times. 292094 Ticks Array.Reverse (Length: 1000) : called 10000 times. 846585 Ticks Xor (Length: 1000) : called 10000 times. 305564115 Ticks String Builder (Length: 100000) : called 10000 times. 74884495 Ticks Array.Reverse (Length: 100000) : called 10000 times. 125409674 Ticks Xor (Length: 100000) : called 10000 times.
对于短字符串来说,Xor似乎更快.
如果字符串包含Unicode数据(严格来说,非BMP字符),则已发布的其他方法将损坏它,因为在反转字符串时无法交换高和低代理代码单元的顺序.(有关此内容的更多信息,请访问我的博客.)
以下代码示例将正确反转包含非BMP字符的字符串,例如"\ U00010380\U00010381"(Ugaritic Letter Alpa,Ugaritic Letter Beta).
public static string Reverse(this string input) { if (input == null) throw new ArgumentNullException("input"); // allocate a buffer to hold the output char[] output = new char[input.Length]; for (int outputIndex = 0, inputIndex = input.Length - 1; outputIndex < input.Length; outputIndex++, inputIndex--) { // check for surrogate pair if (input[inputIndex] >= 0xDC00 && input[inputIndex] <= 0xDFFF && inputIndex > 0 && input[inputIndex - 1] >= 0xD800 && input[inputIndex - 1] <= 0xDBFF) { // preserve the order of the surrogate pair code units output[outputIndex + 1] = input[inputIndex]; output[outputIndex] = input[inputIndex - 1]; outputIndex++; inputIndex--; } else { output[outputIndex] = input[inputIndex]; } } return new string(output); }
来自3.5框架
public string ReverseString(string srtVarable) { return new string(srtVarable.Reverse().ToArray()); }
好的,为了"不要重复自己",我提供了以下解决方案:
public string Reverse(string text) { return Microsoft.VisualBasic.Strings.StrReverse(text); }
我的理解是这个实现,默认情况下在VB.NET中可用,正确处理Unicode字符.
Greg Beech发布的unsafe
选项确实和它一样快(它是一个就地反转); 但是,正如他在回答中指出的那样,这是一个完全灾难性的想法.
也就是说,我很惊讶有很多共识Array.Reverse
是最快的方法.还有一种unsafe
方法可以返回字符串的反向副本(没有就地反转恶作剧),比Array.Reverse
小字符串的方法要快得多:
public static unsafe string Reverse(string text) { int len = text.Length; // Why allocate a char[] array on the heap when you won't use it // outside of this method? Use the stack. char* reversed = stackalloc char[len]; // Avoid bounds-checking performance penalties. fixed (char* str = text) { int i = 0; int j = i + len - 1; while (i < len) { reversed[i++] = str[j--]; } } // Need to use this overload for the System.String constructor // as providing just the char* pointer could result in garbage // at the end of the string (no guarantee of null terminator). return new string(reversed, 0, len); }
以下是一些基准测试结果.
Array.Reverse
随着字符串变大,您可以看到性能增益收缩然后消失.但是对于中小型琴弦来说,很难击败这种方法.
简单而好的答案是使用扩展方法:
static class ExtentionMethodCollection { public static string Inverse(this string @base) { return new string(@base.Reverse().ToArray()); } }
这是输出:
string Answer = "12345".Inverse(); // = "54321"
如果你想玩一个非常危险的游戏,那么这是迄今为止最快的方式(比Array.Reverse
方法快四倍).这是使用指针的就地反向.
请注意,我真的不推荐这个用于任何用途(看看这里有一些原因,你不应该使用这个方法),但是看到它可以完成,并且字符串不是真正不可变的,这很有趣一旦你打开不安全的代码.
public static unsafe string Reverse(string text) { if (string.IsNullOrEmpty(text)) { return text; } fixed (char* pText = text) { char* pStart = pText; char* pEnd = pText + text.Length - 1; for (int i = text.Length / 2; i >= 0; i--) { char temp = *pStart; *pStart++ = *pEnd; *pEnd-- = temp; } return text; } }
在这里查看维基百科条目.它们实现String.Reverse扩展方法.这允许您编写如下代码:
string s = "olleh"; s.Reverse();
他们还使用ToCharArray/Reverse组合,这个问题的其他答案表明.源代码如下所示:
public static string Reverse(this string input) { char[] chars = input.ToCharArray(); Array.Reverse(chars); return new String(chars); }
首先,您不需要调用ToCharArray
字符串已经可以索引为char数组,因此这将为您节省分配.
下一个优化是使用a StringBuilder
来防止不必要的分配(因为字符串是不可变的,连接它们每次都会生成字符串的副本).为了进一步优化这个,我们预先设置了StringBuilder
它的长度,因此不需要扩展它的缓冲区.
public string Reverse(string text) { if (string.IsNullOrEmpty(text)) { return text; } StringBuilder builder = new StringBuilder(text.Length); for (int i = text.Length - 1; i >= 0; i--) { builder.Append(text[i]); } return builder.ToString(); }
编辑:效果数据
我使用Array.Reverse
以下简单程序测试了这个函数和函数,其中Reverse1
一个是函数,另一个函数Reverse2
是:
static void Main(string[] args) { var text = "abcdefghijklmnopqrstuvwxyz"; // pre-jit text = Reverse1(text); text = Reverse2(text); // test var timer1 = Stopwatch.StartNew(); for (var i = 0; i < 10000000; i++) { text = Reverse1(text); } timer1.Stop(); Console.WriteLine("First: {0}", timer1.ElapsedMilliseconds); var timer2 = Stopwatch.StartNew(); for (var i = 0; i < 10000000; i++) { text = Reverse2(text); } timer2.Stop(); Console.WriteLine("Second: {0}", timer2.ElapsedMilliseconds); Console.ReadLine(); }
事实证明,对于短弦乐队来说,这种Array.Reverse
方法的速度大约是上面的两倍,而对于较长的琴弦,差异甚至更明显.因此,鉴于该Array.Reverse
方法既简单又快捷,我建议您使用它而不是这个.我把这个留在这里只是为了表明这不是你应该这样做的方式(令我惊讶的是!)
尝试使用Array.Reverse
public string Reverse(string str) { char[] array = str.ToCharArray(); Array.Reverse(array); return new string(array); }
public static string Reverse(string input) { return string.Concat(Enumerable.Reverse(input)); }
当然,您可以使用Reverse方法扩展字符串类
public static class StringExtensions { public static string Reverse(this string input) { return string.Concat(Enumerable.Reverse(input)); } }
不要打扰功能,只需这样做.注意:第二行将在某些VS版本的立即窗口中引发参数异常.
string s = "Blah"; s = new string(s.ToCharArray().Reverse().ToArray());
"最好的"可以取决于许多事情,但这里有一些从快到慢排序的简短替代品:
string s = "z?a?l?g?o?", pattern = @"(?s).(?<=(?:.(?=.*$(?<=((\P{M}\p{C}?\p{M}*)\1?))))*)"; string s1 = string.Concat(s.Reverse()); // "???o?g?l?a?z" string s2 = Microsoft.VisualBasic.Strings.StrReverse(s); // "o?g?l?a??z" string s3 = string.Concat(StringInfo.ParseCombiningCharacters(s).Reverse() .Select(i => StringInfo.GetNextTextElement(s, i))); // "o?g?l?a?z?" string s4 = Regex.Replace(s, pattern, "$2").Remove(s.Length); // "o?g?l?a?z?"
抱歉,很长的帖子,但这可能很有趣
using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; namespace ConsoleApplication1 { class Program { public static string ReverseUsingArrayClass(string text) { char[] chars = text.ToCharArray(); Array.Reverse(chars); return new string(chars); } public static string ReverseUsingCharacterBuffer(string text) { char[] charArray = new char[text.Length]; int inputStrLength = text.Length - 1; for (int idx = 0; idx <= inputStrLength; idx++) { charArray[idx] = text[inputStrLength - idx]; } return new string(charArray); } public static string ReverseUsingStringBuilder(string text) { if (string.IsNullOrEmpty(text)) { return text; } StringBuilder builder = new StringBuilder(text.Length); for (int i = text.Length - 1; i >= 0; i--) { builder.Append(text[i]); } return builder.ToString(); } private static string ReverseUsingStack(string input) { StackresultStack = new Stack (); foreach (char c in input) { resultStack.Push(c); } StringBuilder sb = new StringBuilder(); while (resultStack.Count > 0) { sb.Append(resultStack.Pop()); } return sb.ToString(); } public static string ReverseUsingXOR(string text) { char[] charArray = text.ToCharArray(); int length = text.Length - 1; for (int i = 0; i < length; i++, length--) { charArray[i] ^= charArray[length]; charArray[length] ^= charArray[i]; charArray[i] ^= charArray[length]; } return new string(charArray); } static void Main(string[] args) { string testString = string.Join(";", new string[] { new string('a', 100), new string('b', 101), new string('c', 102), new string('d', 103), }); int cycleCount = 100000; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); for (int i = 0; i < cycleCount; i++) { ReverseUsingCharacterBuffer(testString); } stopwatch.Stop(); Console.WriteLine("ReverseUsingCharacterBuffer: " + stopwatch.ElapsedMilliseconds + "ms"); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < cycleCount; i++) { ReverseUsingArrayClass(testString); } stopwatch.Stop(); Console.WriteLine("ReverseUsingArrayClass: " + stopwatch.ElapsedMilliseconds + "ms"); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < cycleCount; i++) { ReverseUsingStringBuilder(testString); } stopwatch.Stop(); Console.WriteLine("ReverseUsingStringBuilder: " + stopwatch.ElapsedMilliseconds + "ms"); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < cycleCount; i++) { ReverseUsingStack(testString); } stopwatch.Stop(); Console.WriteLine("ReverseUsingStack: " + stopwatch.ElapsedMilliseconds + "ms"); stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < cycleCount; i++) { ReverseUsingXOR(testString); } stopwatch.Stop(); Console.WriteLine("ReverseUsingXOR: " + stopwatch.ElapsedMilliseconds + "ms"); } } }
结果:
ReverseUsingCharacterBuffer:346ms
ReverseUsingArrayClass:87毫秒
ReverseUsingStringBuilder:824ms
ReverseUsingStack:2086ms
ReverseUsingXOR:319ms