关于c#:反转字符串的最佳方法

Best way to reverse a string

我刚在C 2.0中编写了一个字符串反转函数(即Linq不可用),并得出了以下结论:

1
2
3
4
5
6
7
8
9
10
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;
}

就我个人而言,我并不热衷于这个功能,我相信有更好的方法可以做到这一点。有?


1
2
3
4
5
6
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等)或甚至是代码点(特别注意代理对的反转)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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进行图形集群迭代,从那以后就一直存在,但看起来有点"隐藏"。


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

我建议使用array.reverse,因为它是本地编码的,维护和理解非常简单。

在我测试的所有情况下,它似乎都优于StringBuilder。

1
2
3
4
5
6
7
8
9
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的字符串长度,还有第二种方法可以更快。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    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方法之间的性能比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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();
        }
    }
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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字符的字符串,例如"u00010380u00010381"(ugaritic letter alpa,ugaritic letter beta)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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框架

1
2
3
4
public string ReverseString(string srtVarable)
{
    return new string(srtVarable.Reverse().ToArray());
}


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

1
2
3
4
public string Reverse(string text)
{
   return Microsoft.VisualBasic.Strings.StrReverse(text);
}

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


Greg Beech发布了一个unsafe期权,它确实是尽可能快的(这是一个就地逆转);但正如他在回答中指出的那样,这是一个完全灾难性的想法。

也就是说,我很惊讶有这么多的共识,以至于Array.Reverse是最快的方法。仍然有一种unsafe方法返回字符串的反转副本(没有就地的反转恶作剧),其速度明显快于小字符串的Array.Reverse方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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方法的性能增益会收缩,然后消失。不过,对于中小型的弦来说,很难克服这种方法。


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

1
2
3
4
5
6
7
static class ExtentionMethodCollection
{
    public static string Inverse(this string @base)
    {
        return new string(@base.Reverse().ToArray());
    }
}

下面是输出:

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


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

请注意,我确实不建议将此方法用于任何用途(请在这里查看一下为什么不应使用此方法的一些原因),但有趣的是,可以这样做,而且一旦打开不安全的代码,字符串就不是真正不变的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
    }
}


首先,您不需要调用ToCharArray,因为字符串已经可以作为char数组进行索引,所以这样可以节省分配。

下一个优化是使用StringBuilder来防止不必要的分配(因为字符串是不可变的,每次连接它们都会复制字符串)。为了进一步优化,我们预先设置了StringBuilder的长度,这样它就不需要扩展缓冲区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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是另一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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方法既简单又快速,我建议您使用它,而不是使用这个方法。我把这个放在这里只是为了表明你不应该这样做(令我吃惊的是!)


看看这里的维基百科条目。它们实现了string.reverse扩展方法。这允许您编写这样的代码:

1
2
string s ="olleh";
s.Reverse();

他们还使用了Tochararray/Reverse组合,这是对这个问题的其他答案所建议的。源代码如下所示:

1
2
3
4
5
6
public static string Reverse(this string input)
{
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}


尝试使用数组。反转

1
2
3
4
5
6
public string Reverse(string str)
{
    char[] array = str.ToCharArray();
    Array.Reverse(array);
    return new string(array);
}


1
2
3
4
public static string Reverse(string input)
{
    return string.Concat(Enumerable.Reverse(input));
}

当然,可以用reverse方法扩展字符串类

1
2
3
4
5
6
7
public static class StringExtensions
{
    public static string Reverse(this string input)
    {
        return string.Concat(Enumerable.Reverse(input));
    }
}


不要为函数费心,只要在适当的地方完成它。注意:第二行将在某些vs版本的即时窗口中引发参数异常。

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


必须提交递归示例:

1
2
3
4
5
6
7
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));
}


"最佳"可能取决于许多方面,但以下是从快速到缓慢订购的几个较短的替代品:

1
2
3
4
5
6
7
8
9
10
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?"  ??

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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("
**** For size: {0} ****
"
, 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

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

尺寸:100

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

尺寸:1000

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

尺寸:10000

1
2
3
4
Naive Ticks: 32
Efficient linear Ticks: 28
Recursive Ticks: 84808
DotNet Reverse Ticks: 33

很抱歉发了这么长的帖子,但这可能很有趣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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");            
        }
    }
}

结果:

  • 反向使用字符缓冲:346ms
  • 反向使用RAYClass:87ms
  • 反向使用RingBuilder:824ms
  • 反向使用堆栈:2086ms
  • 反向使用xor:319ms


基于堆栈的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
    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);
    }

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

我从microsoft.visualbasic.strings制作了一个C端口。我不知道他们为什么把这样有用的函数(来自vb)放在系统外。在框架中是字符串,但仍在microsoft.visualBasic下。财务职能的相同情况(如Microsoft.VisualBasic.Financial.Pmt())。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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();
}


怎么样:

1
2
3
4
5
    private string Reverse(string stringToReverse)
    {
        char[] rev = stringToReverse.Reverse().ToArray();
        return new string(rev);
    }


很抱歉在这条旧线索上发帖。我正在练习面试的一些规则。

这就是我为C想出的主意。重构前的第一个版本很糟糕。

1
2
3
4
5
6
7
8
9
10
11
12
13
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开始变快,并且最终在速度上占据相当大的优势。我只是想大致指出速度开始改变的地方。

1
2
3
4
5
6
7
8
static String Reverse(string str)
{    
    char[] charA = str.ToCharArray();

    Array.Reverse(charA);

    return new String(charA);
}

在字符串中有100个字符时,它比我的版本x 4快。但是,如果我知道字符串总是少于13个字符,我将使用我制作的字符串。

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


如果在面试中你被告知不能使用数组,相反,我认为这可能是最快的方法之一。它不创建新的字符串,只迭代数组的一半(即O(N/2)迭代)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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);
    }


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

1
2
3
4
5
6
7
8
9
10
11
12
    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);
    }


"更好的方法"取决于在您的情况、性能、优雅度、可维护性等方面对您更重要的是什么。

无论如何,这里有一个使用数组的方法。反转:

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

string reversed = new string(charArray);

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

下面是该函数的Unicode安全版本,作为安全处理Unicode的扩展编写。它接近标记的完整答案,但不会引发"无效的高代理字符"的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);

    }
}

使用子字符串怎么样

1
2
3
4
5
6
7
8
9
10
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;
}


就这么简单:

1
2
3
4
5
6
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:
 {0}"
, x1);

参见输出。


1
2
3
4
5
6
7
8
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);
        }

首先,您需要了解的是str+=将调整字符串内存的大小,为额外的1个字符腾出空间。这很好,但是如果你有一本1000页的书想要倒转,这需要很长时间才能完成。

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

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

1
2
3
4
5
6
            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[]初始化时有一个初始内存分配,当字符串构造函数从char数组构建字符串时有一个分配。

在我的系统中,我为您运行了一个测试,它将反转一个由27500000个字符组成的字符串。以下是10次执行的结果:

StringBuilder:190K-200K勾号

字符数组:130k-160k滴答

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

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

干杯


1
2
3
4
5
6
7
8
9
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;
        }

比上述方法更快

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
        }

最简单的方法:

1
string reversed = new string(text.Reverse().ToArray());

1
2
    string original ="Stack Overflow";
    string reversed = new string(original.Reverse().ToArray());


有多种方法可以反转字符串,我在下面展示了其中的3种。

--使用array.reverse函数。

1
2
3
4
5
6
 private static string ReverseString1(string text)
    {
        char[] rtext = text.ToCharArray();
        Array.Reverse(rtext);
        return new string(rtext);
    }

--仅使用字符串

1
2
3
4
5
6
7
8
9
  private static string ReverseString2(string text)
    {
        String rtext ="";
        for (int i = text.Length - 1; i >= 0; i--)
        {
            rtext = rtext + text[i];
        }
        return rtext;
    }

--仅使用字符数组

1
2
3
4
5
6
7
8
9
10
 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);
    }

在采访中我也被问到类似的问题。这是我的回答,尽管它的表现可能不如其他答案快。我的问题的措词是"make a class that can have a method to print a string backwards",

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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);
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
    }
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}


这很简单

1
2
3
4
5
6
7
8
9
static void Reverse()
    {
        string str ="PankajRawat";
        var arr = str.ToCharArray();
        for (int i = str.Length-1; i >= 0; i--)
        {
            Console.Write(arr[i]);
        }
    }

1
2
3
4
5
6
7
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));
}


甚至不用新字符串来反转字符串。让我们说

1
2
3
4
5
6
7
8
9
10
11
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--];
}


SELECT REVERSE('somestring');完成。