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

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


当前回答

如果字符串包含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);
}

其他回答

从。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方法使用的一半内存。

选择倒档(“somestring”); 完成了。

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

这是我为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);
}

对比数组。相反的方法下面,它出现更快的12个字符或更少的字符串。13个字符后,数组。倒车开始变得更快,最终在速度上占主导地位。我只是想指出速度开始变化的大致位置。

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

    Array.Reverse(charA);

    return new String(charA);
}

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

测试使用Stopwatch进行,并进行了500万次迭代。此外,我不确定我的版本是否处理代理或组合字符情况与Unicode编码。

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

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

首先,你不需要调用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测试了这个函数和这个函数。用下面的简单程序进行反向,其中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();
}

事实证明,对于短字符串数组。反向方法大约是上面方法的两倍,对于更长的字符串,差异甚至更明显。已知这个数组。反向法既简单又快,我建议你用那个而不是这个。我把这个留在这里只是为了表明这不是你应该做的方式(让我很惊讶!)