反转字符串的最佳方法

我刚刚在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;
}

就我个人而言,我并不喜欢这个功能,我相信有更好的方法来实现它。是吗?

919346 次浏览

看看维基百科上的条目在这里。它们实现了字符串。反向扩展法。这允许你编写这样的代码:

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数组,所以这将节省你的分配。

下一个优化是使用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方法既简单又快速,我建议你使用它而不是这个。我把这个留在这里只是为了表明这不是你应该做的方式(让我很惊讶!)

public static string Reverse( string s )
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}

这是一个非常棘手的问题。

我推荐使用Array。大多数情况下是反向的,因为它是原生编码的,维护和理解非常简单。

在我测试的所有情况下,它似乎都优于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之间的性能比较。逆向和异或法。

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似乎更快。

“更好的方式”取决于在你的情况下什么对你更重要,性能、优雅性、可维护性等等。

总之,这里有一个使用数组的方法。相反:

string inputString="The quick brown fox jumps over the lazy dog.";
char[] charArray = inputString.ToCharArray();
Array.Reverse(charArray);


string reversed = new string(charArray);

尝试使用Array。反向


public string Reverse(string str)
{
char[] array = str.ToCharArray();
Array.Reverse(array);
return new string(array);
}

必须提交一个递归的例子:

private static string Reverse(string str)
{
if (str.IsNullOrEmpty(str) || str.Length == 1)
return str;
else
return str[str.Length - 1] + Reverse(str.Substring(0, str.Length - 1));
}

抱歉写了这么长时间,但这可能会很有趣

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)
{
Stack<char> resultStack = new Stack<char>();
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: 346毫秒
  • ReverseUsingArrayClass: 87毫秒
  • ReverseUsingStringBuilder: 824毫秒
  • ReverseUsingStack: 2086毫秒
  • ReverseUsingXOR: 319毫秒

如果你想玩一个非常危险的游戏,那么这是目前为止最快的方法(比Array.Reverse方法快4倍左右)。这是使用指针的原地反向。

请注意,我真的不建议在任何情况下使用这种方法(看看为什么你不应该使用这种方法的一些原因吧),但有趣的是,可以这样做,而且一旦打开不安全代码,字符串就不是真正不可变的。

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

如果字符串包含Unicode数据(严格地说,非bmp字符),其他已经发布的方法将破坏它,因为在反转字符串时不能交换高和低代理代码单元的顺序。(更多信息可以在我的博客上找到。)

下面的代码示例将正确地反转包含非bmp字符的字符串,例如,“\U00010380\U00010381”(ugartic Letter Alpa, ugartic 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);
}
public string rev(string str)
{
if (str.Length <= 0)
return string.Empty;
else
return str[str.Length-1]+ rev(str.Substring(0,str.Length-1));
}

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

# EYZ0。

您可以看到,对于Array.Reverse方法,随着字符串变大,性能增益会缩小,然后消失。然而,对于小型到中型的字符串,很难击败这种方法。

如何:

    private string Reverse(string stringToReverse)
{
char[] rev = stringToReverse.Reverse().ToArray();
return new string(rev);
}

好吧,为了“不要重复你自己”,我提供以下解决方案:

public string Reverse(string text)
{
return Microsoft.VisualBasic.Strings.StrReverse(text);
}

我的理解是这个实现,在VB中默认可用。NET,正确地处理Unicode字符。

如果在面试中你被告知不能使用Array。反过来,我想这可能是最快的一个。它不会创建新的字符串,只迭代数组的一半以上(即O(n/2)次迭代)

    public static string ReverseString(string stringToReverse)
{
char[] charArray = stringToReverse.ToCharArray();
int len = charArray.Length-1;
int mid = len / 2;


for (int i = 0; i < mid; i++)
{
char tmp = charArray[i];
charArray[i] = charArray[len - i];
charArray[len - i] = tmp;
}
return new string(charArray);
}
public static string Reverse2(string x)
{
char[] charArray = new char[x.Length];
int len = x.Length - 1;
for (int i = 0; i <= len; i++)
charArray[i] = x[len - i];
return new string(charArray);
}
public string Reverse(string input)
{
char[] output = new char[input.Length];


int forwards = 0;
int backwards = input.Length - 1;


do
{
output[forwards] = input[backwards];
output[backwards] = input[forwards];
}while(++forwards <= --backwards);


return new String(output);
}


public string DotNetReverse(string input)
{
char[] toReverse = input.ToCharArray();
Array.Reverse(toReverse);
return new String(toReverse);
}


public string NaiveReverse(string input)
{
char[] outputArray = new char[input.Length];
for (int i = 0; i < input.Length; i++)
{
outputArray[i] = input[input.Length - 1 - i];
}


return new String(outputArray);
}


public string RecursiveReverse(string input)
{
return RecursiveReverseHelper(input, 0, input.Length - 1);
}


public string RecursiveReverseHelper(string input, int startIndex , int endIndex)
{
if (startIndex == endIndex)
{
return "" + input[startIndex];
}


if (endIndex - startIndex == 1)
{
return "" + input[endIndex] + input[startIndex];
}


return input[endIndex] + RecursiveReverseHelper(input, startIndex + 1, endIndex - 1) + input[startIndex];
}




void Main()
{
int[] sizes = new int[] { 10, 100, 1000, 10000 };
for(int sizeIndex = 0; sizeIndex < sizes.Length; sizeIndex++)
{
string holaMundo  = "";
for(int i = 0; i < sizes[sizeIndex]; i+= 5)
{
holaMundo += "ABCDE";
}


string.Format("\n**** For size: {0} ****\n", sizes[sizeIndex]).Dump();


string odnuMaloh = DotNetReverse(holaMundo);


var stopWatch = Stopwatch.StartNew();
string result = NaiveReverse(holaMundo);
("Naive Ticks: " + stopWatch.ElapsedTicks).Dump();


stopWatch.Restart();
result = Reverse(holaMundo);
("Efficient linear Ticks: " + stopWatch.ElapsedTicks).Dump();


stopWatch.Restart();
result = RecursiveReverse(holaMundo);
("Recursive Ticks: " + stopWatch.ElapsedTicks).Dump();


stopWatch.Restart();
result = DotNetReverse(holaMundo);
("DotNet Reverse Ticks: " + stopWatch.ElapsedTicks).Dump();
}
}

输出

尺寸:10

Naive Ticks: 1
Efficient linear Ticks: 0
Recursive Ticks: 2
DotNet Reverse Ticks: 1

尺寸:100

Naive Ticks: 2
Efficient linear Ticks: 1
Recursive Ticks: 12
DotNet Reverse Ticks: 1

规格:1000

Naive Ticks: 5
Efficient linear Ticks: 2
Recursive Ticks: 358
DotNet Reverse Ticks: 9

尺寸:10000

Naive Ticks: 32
Efficient linear Ticks: 28
Recursive Ticks: 84808
DotNet Reverse Ticks: 33
private static string Reverse(string str)
{
string revStr = string.Empty;
for (int i = str.Length - 1; i >= 0; i--)
{
revStr += str[i].ToString();
}
return revStr;
}

比上述方法快

private static string ReverseEx(string str)
{
char[] chrArray = str.ToCharArray();
int len = chrArray.Length - 1;
char rev = 'n';
for (int i = 0; i <= len/2; i++)
{
rev = chrArray[i];
chrArray[i] = chrArray[len - i];
chrArray[len - i] = rev;
}
return new string(chrArray);
}

这里有一个解决方案,正确地将字符串"Les Mise\u0301rables"反转为"selbare\u0301siM seL"。这应该像selbarésiM seL一样呈现,而不是selbaŕesiM seL(注意重音的位置),正如大多数基于代码单元(Array.Reverse等)甚至代码点的实现的结果(对代理项对进行特别注意的反转)。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;


public static class Test
{
private static IEnumerable<string> GraphemeClusters(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,它一直都在那里,但看起来有点“隐藏”。

不要为函数而烦恼,只要在适当的地方做就可以了。注意:第二行会在一些VS版本的即时窗口中抛出一个参数异常。

string s = "Blah";
s = new string(s.ToCharArray().Reverse().ToArray());

如果你可以使用LINQ(。NET Framework 3.5+)而不是只跟随一行代码就可以得到简短的代码。不要忘记添加using System.Linq;来访问Enumerable.Reverse:

public string ReverseString(string srtVarable)
{
return new string(srtVarable.Reverse().ToArray());
}

注:

  • 不是最快的版本——根据马丁Niederl,比这里最快的版本慢5.7倍。
  • 这段代码和许多其他选项一样完全忽略了所有类型的多字符组合,因此限制在家庭作业和包含的字符串中使用。请参阅本问题中的另一个回答,以了解正确处理此类组合的实现。
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));
}
}
string A = null;
//a now is reversed and you can use it
A = SimulateStrReverse.StrReverse("your string");


public static class SimulateStrReverse
{
public static string StrReverse(string expression)
{
if (string.IsNullOrEmpty(expression))
return string.Empty;


string reversedString = string.Empty;
for (int charIndex = expression.Length - 1; charIndex >= 0; charIndex--)
{
reversedString += expression[charIndex];
}
return reversedString;
}
}

基于堆栈的解决方案。

    public static string Reverse(string text)
{
var stack = new Stack<char>(text);
var array = new char[stack.Count];


int i = 0;
while (stack.Count != 0)
{
array[i++] = stack.Pop();
}


return new string(array);
}

    public static string Reverse(string text)
{
var stack = new Stack<char>(text);
return string.Join("", stack);
}

如果字符串只包含ASCII字符,则可以使用此方法。

    public static string ASCIIReverse(string s)
{
byte[] reversed = new byte[s.Length];


int k = 0;
for (int i = s.Length - 1; i >= 0; i--)
{
reversed[k++] = (byte)s[i];
}


return Encoding.ASCII.GetString(reversed);
}
    string original = "Stack Overflow";
string reversed = new string(original.Reverse().ToArray());
< p > # EYZ0 做。< / p >

我从Microsoft.VisualBasic.Strings开始做了一个c#移植。我不知道他们为什么把这么有用的函数(从VB)保存在系统之外。框架中的字符串,但仍然在Microsoft.VisualBasic下。同样的场景用于财务功能(例如Microsoft.VisualBasic.Financial.Pmt())。

public static string StrReverse(this string expression)
{
if ((expression == null))
return "";


int srcIndex;


var length = expression.Length;
if (length == 0)
return "";


//CONSIDER: Get System.String to add a surrogate aware Reverse method


//Detect if there are any graphemes that need special handling
for (srcIndex = 0; srcIndex <= length - 1; srcIndex++)
{
var ch = expression[srcIndex];
var uc = char.GetUnicodeCategory(ch);
if (uc == UnicodeCategory.Surrogate || uc == UnicodeCategory.NonSpacingMark || uc == UnicodeCategory.SpacingCombiningMark || uc == UnicodeCategory.EnclosingMark)
{
//Need to use special handling
return InternalStrReverse(expression, srcIndex, length);
}
}


var chars = expression.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}


///<remarks>This routine handles reversing Strings containing graphemes
/// GRAPHEME: a text element that is displayed as a single character</remarks>
private static string InternalStrReverse(string expression, int srcIndex, int length)
{
//This code can only be hit one time
var sb = new StringBuilder(length) { Length = length };


var textEnum = StringInfo.GetTextElementEnumerator(expression, srcIndex);


//Init enumerator position
if (!textEnum.MoveNext())
{
return "";
}


var lastSrcIndex = 0;
var destIndex = length - 1;


//Copy up the first surrogate found
while (lastSrcIndex < srcIndex)
{
sb[destIndex] = expression[lastSrcIndex];
destIndex -= 1;
lastSrcIndex += 1;
}


//Now iterate through the text elements and copy them to the reversed string
var nextSrcIndex = textEnum.ElementIndex;


while (destIndex >= 0)
{
srcIndex = nextSrcIndex;


//Move to next element
nextSrcIndex = (textEnum.MoveNext()) ? textEnum.ElementIndex : length;
lastSrcIndex = nextSrcIndex - 1;


while (lastSrcIndex >= srcIndex)
{
sb[destIndex] = expression[lastSrcIndex];
destIndex -= 1;
lastSrcIndex -= 1;
}
}


return sb.ToString();
}

首先,你必须理解的是str+=将调整字符串内存大小,为1个额外的字符腾出空间。这很好,但是如果你有一本1000页的书,你想要反转,这将需要很长时间来执行。

有些人建议的解决方案是使用StringBuilder。字符串构建器在执行+=时所做的是分配更大的内存块来保存新字符,这样它就不需要在每次添加字符时进行重新分配。

如果你真的想要一个快速和最小的解决方案,我建议如下:

            char[] chars = new char[str.Length];
for (int i = str.Length - 1, j = 0; i >= 0; --i, ++j)
{
chars[j] = str[i];
}
str = new String(chars);

在这个解决方案中,在初始化char[]时有一个初始内存分配,在string构造函数从char数组构建字符串时有一个初始内存分配。

在我的系统上,我为您运行了一个测试,反转了一个2750,000个字符的字符串。以下是10次执行的结果:

StringBuilder: 190K - 200K tick

字符数组:130K - 160K

我还运行了一个正常String +=的测试,但我在10分钟后放弃了它,没有输出。

但是,我也注意到,对于较小的字符串,StringBuilder更快,因此您必须根据输入来决定实现。

干杯

简单而漂亮的答案是使用扩展方法:

static class ExtentionMethodCollection
{
public static string Inverse(this string @base)
{
return new string(@base.Reverse().ToArray());
}
}

这是输出:

string Answer = "12345".Inverse(); // = "54321"

反转字符串,甚至不使用新字符串。让说

String input  = "Mark Henry";
//Just to convert into char array. One can simply take input in char array.
Char[] array = input.toCharArray(input);
int a = input.length;


for(int i=0; i<(array.length/2 -1) ; i++)
{
array[i] = array[i] + array[a];
array[a] = array[i] - array[a];
array[i] = array[i] - array[a--];
}

很抱歉在这个旧帖子上发帖。我在为面试练习一些代码。

这是我为c#设计的。在重构之前,我的第一个版本非常糟糕。

static String Reverse2(string str)
{
int strLen = str.Length, elem = strLen - 1;
char[] charA = new char[strLen];


for (int i = 0; i < strLen; i++)
{
charA[elem] = str[i];
elem--;
}


return new String(charA);
}

与下面的Array.Reverse方法相比,它在字符串中包含12个字符或更少时显示得更快。在13个字符之后,Array.Reverse开始变得更快,最终在速度上占据主导地位。我只是想指出速度开始变化的大致位置。

static String Reverse(string str)
{
char[] charA = str.ToCharArray();


Array.Reverse(charA);


return new String(charA);
}

在字符串中有100个字符时,它比我的版本x 4快。然而,如果我知道字符串总是小于13个字符,我就会使用我所创建的字符串。

测试使用Stopwatch和5000000次迭代完成。此外,我不确定我的版本是否处理代理或组合字符情况与Unicode编码。

public static string reverse(string s)
{
string r = "";
for (int i = s.Length; i > 0; i--) r += s[i - 1];
return r;
}

有各种方法来反转字符串,我已经在下面展示了其中的3种。

——使用数组。反向作用。

 private static string ReverseString1(string text)
{
char[] rtext = text.ToCharArray();
Array.Reverse(rtext);
return new string(rtext);
}

——只使用字符串

  private static string ReverseString2(string text)
{
String rtext = "";
for (int i = text.Length - 1; i >= 0; i--)
{
rtext = rtext + text[i];
}
return rtext;
}

——只使用char数组

 public static string ReverseString3(string str)
{
char[] chars = str.ToCharArray();
char[] rchars = new char[chars.Length];
for (int i = 0, j = str.Length - 1; i < chars.Length; i++, j--)
{
rchars[j] = chars[i];
}
return new string(rchars);
}

我在面试中也被问到类似的问题。这是我的回答,尽管它在性能上可能没有其他答案那么快。我的问题是“创建一个类,它可以有一个反向打印字符串的方法”:

using System;
using System.Collections.Generic;
using System.Linq;


namespace BackwardsTest
{
class PrintBackwards
{
public static void print(string param)
{
if (param == null || param.Length == 0)
{
Console.WriteLine("string is null");
return;
}
List<char> list = new List<char>();
string returned = null;
foreach(char d in param)
{
list.Add(d);
}
for(int i = list.Count(); i > 0; i--)
{
returned = returned + list[list.Count - 1];
list.RemoveAt(list.Count - 1);
}
Console.WriteLine(returned);
}
}
class Program
{
static void Main(string[] args)
{
string test = "I want to print backwards";
PrintBackwards.print(test);
System.Threading.Thread.Sleep(5000);
}
}
}

就这么简单:

string x = "your string";
string x1 = "";
for(int i = x.Length-1 ; i >= 0; i--)
x1 += x[i];
Console.WriteLine("The reverse of the string is:\n {0}", x1);

请看输出

“最佳”取决于很多东西,但这里有一些从快到慢的简短选择:

string s = "z̽a̎l͘g̈o̓😀😆", pattern = @"(?s).(?<=(?:.(?=.*$(?<=((\P{M}\p{C}?\p{M}*)\1?))))*)";


string s1 = string.Concat(s.Reverse());                          // "☐😀☐̓ö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̽"  👍

下面是该函数的unicode安全版本,编写为将安全处理unicode的扩展。它接近标记的完整答案,但不会抛出“无效的高代理字符”异常。

public static class StringExtensions
{
public static string Reverse(this string s)
{
var info = new StringInfo(s);
var charArray = new char[s.Length];
var teIndices = StringInfo.ParseCombiningCharacters(s).Reverse();


int j = 0;
foreach(var i in  teIndices)
{
if (char.IsHighSurrogate(s[i]))
{
charArray[j] = s[i];
j++;
charArray[j] = s[i+1];
}
else
{
charArray[j] = s[i];
}
j++;
}


return new string(charArray);


}
}

使用Substring怎么样

static string ReverseString(string text)
{
string sub = "";
int indexCount = text.Length - 1;
for (int i = indexCount; i > -1; i--)
{
sub = sub + text.Substring(i, 1);
}
return sub;
}

这是用于反向字符串的代码

public Static void main(){
string text = "Test Text";
Console.Writeline(RevestString(text))
}


public Static string RevestString(string text){
char[] textToChar = text.ToCharArray();
string result= string.Empty;
int length = textToChar .Length;
for (int i = length; i > 0; --i)
result += textToChar[i - 1];
return result;
}

非常简单

static void Reverse()
{
string str = "PankajRawat";
var arr = str.ToCharArray();
for (int i = str.Length-1; i >= 0; i--)
{
Console.Write(arr[i]);
}
}

最简单的方法:

string reversed = new string(text.Reverse().ToArray());
static void Main(string[] args)
{
string str = "";
string reverse = "";
Console.WriteLine("Enter the value to reverse");
str = Console.ReadLine();
int length = 0;
length = str.Length - 1;
while(length >= 0)
{
reverse = reverse + str[length];
length--;
}
Console.Write("Reverse string is {0}", reverse);
Console.ReadKey();
}

从。net Core 2.1开始,有一种新的方法可以使用string.Create方法来反转字符串。

请注意,这个解决方案不能正确处理Unicode组合字符等,如“Les Mise\u0301rables"将被转换为“selbarésiM seL"”参见其他答案以获得更好的解决方案。

public static string Reverse(string input)
{
return string.Create<string>(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}

这实际上是将input的字符复制到一个新字符串中,并在适当的位置反转新字符串。

为什么string.Create有用?

当从现有数组创建字符串时,将分配一个新的内部数组并复制其值。否则,就有可能在创建字符串之后(在安全的环境中)对其进行变异。也就是说,在下面的代码片段中,我们必须分配一个长度为10的数组两次,一次作为缓冲区,一次作为字符串的内部数组。

var chars = new char[10];
// set array values
var str = new string(chars);

string.Create实际上允许我们在字符串创建期间操作内部数组。也就是说,我们不再需要缓冲区,因此可以避免分配一个字符数组。

史蒂夫·戈登写了更详细的在这里。还有一篇关于MSDN的文章。

如何使用string.Create?

public static string Create<TState>(int length, TState state, SpanAction<char, TState> action);

该方法有三个参数:

  1. 要创建的字符串长度,
  2. 你想用来动态创建新字符串的数据,
  3. 以及从数据创建最终字符串的委托,其中第一个参数指向新字符串的内部char数组,第二个参数是传递给string.Create的数据(状态)。

在委托内部,我们可以指定如何从数据中创建新字符串。在我们的例子中,我们只是将输入字符串的字符复制到新字符串使用的Span。然后反转Span,因此整个字符串被反转。

基准

为了比较我提出的反转字符串的方法与已接受的答案,我使用BenchmarkDotNet编写了两个基准测试。

public class StringExtensions
{
public static string ReverseWithArray(string input)
{
var charArray = input.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}


public static string ReverseWithStringCreate(string input)
{
return string.Create(input.Length, input, (chars, state) =>
{
state.AsSpan().CopyTo(chars);
chars.Reverse();
});
}
}


[MemoryDiagnoser]
public class StringReverseBenchmarks
{
private string input;


[Params(10, 100, 1000)]
public int InputLength { get; set; }




[GlobalSetup]
public void SetInput()
{
// Creates a random string of the given length
this.input = RandomStringGenerator.GetString(InputLength);
}


[Benchmark(Baseline = true)]
public string WithReverseArray() => StringExtensions.ReverseWithArray(input);


[Benchmark]
public string WithStringCreate() => StringExtensions.ReverseWithStringCreate(input);
}

下面是我的测试结果:

| Method           | InputLength |         Mean |      Error |    StdDev |  Gen 0 | Allocated |
| ---------------- | ----------- | -----------: | ---------: | --------: | -----: | --------: |
| WithReverseArray | 10          |    45.464 ns |  0.4836 ns | 0.4524 ns | 0.0610 |      96 B |
| WithStringCreate | 10          |    39.749 ns |  0.3206 ns | 0.2842 ns | 0.0305 |      48 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 100         |   175.162 ns |  2.8766 ns | 2.2458 ns | 0.2897 |     456 B |
| WithStringCreate | 100         |   125.284 ns |  2.4657 ns | 2.0590 ns | 0.1473 |     232 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 1000        | 1,523.544 ns |  9.8808 ns | 8.7591 ns | 2.5768 |    4056 B |
| WithStringCreate | 1000        | 1,078.957 ns | 10.2948 ns | 9.6298 ns | 1.2894 |    2032 B |

正如您所看到的,使用ReverseWithStringCreate,我们只分配了ReverseWithArray方法使用的一半内存。

     using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace ConsoleApplication1
{
class Program
{
public static string ReverseString(string str)
{
int totalLength = str.Length;
int iCount = 0;
string strRev = string.Empty;
iCount = totalLength;
while (iCount != 0)
{
iCount--;
strRev += str[iCount];
}
return strRev;
}
static void Main(string[] args)
{
string str = "Punit Pandya";
string strResult = ReverseString(str);
Console.WriteLine(strResult);
Console.ReadLine();
}
}


}

使用LINQ的聚合函数

string s = "Karthik U";
s = s.Aggregate(new StringBuilder(), (o, p) => o.Insert(0, p)).ToString();

因为我喜欢两个答案——一个是使用string.Create,因此高性能和低分配;另一个是使用StringInfo类,所以我决定需要一种组合方法。这是最终的字符串反转方法:)

private static string ReverseString(string str)
{
return string.Create(str.Length, str, (chars, state) =>
{
var enumerator = StringInfo.GetTextElementEnumerator(state);
var position = state.Length;
while (enumerator.MoveNext())
{
var cluster = ((string)enumerator.Current).AsSpan();
cluster.CopyTo(chars.Slice(position - cluster.Length));
position -= cluster.Length;
}
});
}

还有一种更好的方法,使用StringInfo类的方法,它通过只返回索引来跳过Enumerator的大量字符串分配。

private static string ReverseString(string str)
{
return string.Create(str.Length, str, (chars, state) =>
{
var position = 0;
var indexes = StringInfo.ParseCombiningCharacters(state); // skips string creation
var stateSpan = state.AsSpan();
for (int len = indexes.Length, i = len - 1; i >= 0; i--)
{
var index = indexes[i];
var spanLength = i == len - 1 ? state.Length - index : indexes[i + 1] - index;
stateSpan.Slice(index, spanLength).CopyTo(chars.Slice(position));
position += spanLength;
}
});
}

与LINQ解决方案相比的一些基准测试:

String length 20:


LINQ                       Mean: 2,355.5 ns   Allocated: 1440 B
string.Create              Mean:   851.0 ns   Allocated:  720 B
string.Create with indexes Mean:   466.4 ns   Allocated:  168 B


String length 450:


LINQ                          Mean: 34.33 us   Allocated: 22.98 KB
string.Create                 Mean:   19.13 us   Allocated: 14.98 KB
string.Create with indexes    Mean:   10.32 us   Allocated: 2.69 KB

处理所有类型的unicode字符

使用System.Globalization;

    public static string ReverseString(this string content) {


var textElementEnumerator = StringInfo.GetTextElementEnumerator(content);


var SbBuilder = new StringBuilder(content.Length);


while (textElementEnumerator.MoveNext()) {
SbBuilder.Insert(0, textElementEnumerator.GetTextElement());
}


return SbBuilder.ToString();
}

如果有人问关于字符串反向的问题,其目的可能是想知道你是否知道任何位操作,比如异或。c#中有数组。反向函数,但是,您可以使用简单的异或操作在几行代码(最少)

    public static string MyReverse(string s)
{
char[] charArray = s.ToCharArray();
int bgn = -1;
int end = s.Length;
while(++bgn < --end)
{
charArray[bgn] ^= charArray[end];
charArray[end] ^= charArray[bgn];
charArray[bgn] ^= charArray[end];
}
return new string(charArray);
}

我们可以使用两个指针,一个指向字符串的开始,另一个指向字符串的结束。然后每次交换第i个和第j个指针的值,第i个指针的值递增+1,第j个指针的值递减-1。

string s = Console.ReadLine();
Console.WriteLine(s + "\n");
char[] charArray = s.ToCharArray();
int i = 0, j = s.Length - 1;
while (i < j) {
char temp = charArray[i];
charArray[i] = charArray[j];
charArray[j] = temp;
i++; j--;
}
string ans = new string(charArray);
Console.WriteLine(ans + "\n");
// Input: hello
// Output: olleh

使用StringInfo.GetTextElementEnumerator ()的地方有几个正确答案。向你致敬!

现在,让我们找出最有效的方法来使用这个方法。首先,大多数答案涉及调用反向()ToArray (),这在热路径上是一个大禁忌。为了获得最佳性能,我们希望避免分配垃圾。例如,临时字符串,分配器,数组等。

优化的管柱反转

  • 降低气相色谱压力。例如,没有LINQ枚举数,没有数组。
  • 使用# EYZ0
  • 使用# EYZ0
using System.Globalization;


public static class StringExtensions
{
public static string AsReversed(this string s)
{
return string.Create(s.Length, s, (chars, state) =>
{
int i = 0;
var enumerator = StringInfo.GetTextElementEnumerator(s);
while (enumerator.MoveNext())
{
var element = enumerator.GetTextElement();
i += element.Length;
element.CopyTo(chars[^i..]);
}
});
}
}


注意,GetTextElementEnumerator() API在.NET Core 3.1和更早的版本中包含错误。确保运行.NET 5或更高版本!最后,请务必查看正在讨论API改进的问题# 19423