我刚刚在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;
}
就我个人而言,我并不喜欢这个功能,我相信有更好的方法来实现它。是吗?
这里有一个解决方案,正确地将字符串“Les Mise\u0301rables”反转为“selbare\u0301siM seL”。这应该像selbarésiM seL一样呈现,而不是selbaŕesiM seL(注意重音的位置),就像大多数基于代码单元(Array。反向,等等),甚至代码点(反向时要特别注意代理项对)。
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进行字素簇迭代,它一直存在,但看起来有点“隐藏”。
Greg Beech发布了一个不安全的选项,它确实很快(这是一个原地逆转);但是,正如他在回答中指出的那样,这完全是一个灾难性的想法。
也就是说,我很惊讶有这么多的共识,Array。逆向是最快的方法。仍然有一种不安全的方法,它返回字符串的反向副本(没有原地反转的恶作剧),比Array快得多。小字符串的反向方法:
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,性能增益会缩小,然后消失。当字符串变大时,反向方法。然而,对于小型到中型的字符串,很难击败这种方法。
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