我刚刚在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 string Reverse(string text)
{
   return Microsoft.VisualBasic.Strings.StrReverse(text);
}

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

其他回答

尝试使用Array。反向


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

下面是该函数的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);

    }
}

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

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毫秒

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

请注意,这个解决方案不能正确处理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();
    });
}

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

为什么是弦。创建有用吗?

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

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

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

史蒂夫·戈登在这里写了更详细的内容。在MSDN上也有一篇文章。

如何使用string.Create?

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

该方法有三个参数:

要创建的字符串长度, 你想用来动态创建新字符串的数据, 以及从数据创建最终字符串的委托,其中第一个参数指向新字符串的内部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方法使用的一半内存。

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

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

注:

不是最快的版本——根据马丁·尼德尔的说法,比最快的版本慢了5.7倍。 这段代码和许多其他选项一样,完全忽略了所有类型的多字符组合,因此只能在家庭作业和不包含此类字符的字符串中使用。有关正确处理此类组合的实现,请参阅本问题中的另一个答案。