567

I've just had to write a string reverse function in C# 2.0 (i.e. LINQ not available) and came up with this:

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

Personally I'm not crazy about the function and am convinced that there's a better way to do it. Is there?

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Guy
  • 65,082
  • 97
  • 254
  • 325
  • 61
    Surprisingly tricky if you want proper international support. Example: Croatian/Serbian have two-character letters lj, nj etc. Proper reverse of "ljudi" is "idulj", NOT "idujl". I'm sure you'd fare far worse when it comes to Arabic, Thai etc. – dbkk Nov 14 '09 at 10:46
  • 1
    I wonder if it's slower to concat a string instead of initializing a temp array and storing the results in that, and then finally converting that to a string? – The Muffin Man Jun 01 '13 at 05:29
  • 3
    Much newer related thread: [Reverse a string with accent chars?](http://stackoverflow.com/questions/15029238/) – Jeppe Stig Nielsen Jul 23 '13 at 15:15
  • 13
    This question could be improved by defining what you mean by "best". Fastest? Most readable? Most reliable across various edge cases (null checks, multiple languages, etc.)? Most maintainable across versions of C# and .NET? – hypehuman Apr 15 '16 at 14:23
  • 4
    Why there is no inbuilt direct way to do this simple task? – Krishnadas PC Aug 15 '21 at 13:58

53 Answers53

808
public static string Reverse( string s )
{
    char[] charArray = s.ToCharArray();
    Array.Reverse(charArray);
    return new string(charArray);
}
Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
PeteT
  • 18,754
  • 26
  • 95
  • 132
  • 18
    sambo99: It doesn't need to mention unicode: chars in C# are unicode characters, not bytes. Xor may be faster, but apart from being far less readable, that may even be what Array.Reverse() uses internally. – Nick Johnson Oct 23 '08 at 13:18
  • 30
    @Arachnid: Actually, chars in C# are UTF-16 code units; it takes two of them to represent a supplementary character. See http://www.jaggersoft.com/csharp_standard/9.4.1.htm. – Bradley Grainger Oct 23 '08 at 15:18
  • 5
    Yeah sambo99 I suppose you are correct but it's a pretty rare case to use UTF-32. And XOR is only faster for a very small range of values, the correct answer would be to implement different methods for different lengths I suppose. But this is clear and concise which is a benefit in my opinion. – PeteT Dec 08 '08 at 15:52
  • 26
    Unicode control characters makes this method useless for non latin character sets. See Jon Skeet explanation, using a sock puppet: http://codeblog.jonskeet.uk/2009/11/02/omg-ponies-aka-humanity-epic-fail/ (1/4 the way down), or the video: http://vimeo.com/7516539 – Callum Rogers Apr 19 '10 at 23:14
  • 23
    Hope you don't encounter any surrogates or combining characters. – dalle Oct 14 '10 at 19:04
  • Jon Skeet's code does not work as he says. Les miserables reverses correctly. Skeet fail. Unbelievable. – Andrei Rînea Dec 10 '12 at 16:08
  • @AndreiRinea See my recent comment on the question [Reverse a string with accent chars?](http://stackoverflow.com/questions/15029238/). – Jeppe Stig Nielsen Jul 23 '13 at 15:17
  • 1
    @CallumRogers: Sorry to nitpick, but it's not "non latin character sets" - many languages are covered in the base 16 bits range (A.K.A "the base multilingual plane" ([see here](http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane))). These languages include Hebrew, Arabic, and even Chinese and Japanese (surprisingly). It really depends on the use case. For example in my case of reversing Hebrew without Diacritics, the above works just fine. – Amir Abiri Nov 15 '13 at 08:45
  • I found it helpful to create an extension method for the string class from the above. @PeteT perhaps you might mention that in the answer for other people to find. using System; public static class StringExtension { public static string Reverse(this string s) { char[] charArray = s.ToCharArray(); Array.Reverse( charArray ); return new string( charArray ); } } – Adam B Apr 28 '16 at 20:28
  • @BradleyGrainger You really should quote what you are replying to. Notice that the person you replied to ("arachnid") has deleted their comment – barlop Jul 10 '16 at 12:11
  • @CallumRogers You write "Unicode control characters makes this method useless for non latin character sets." <-- what unicode control characters? Your link to jon skeet's article doesn't even mention a word about control characters. He doesn't even say the word 'control'. He's talking about unicode combining characters, such as diacritical marks , they aren't control characters. Things like the french acute accent on the letter 'e'. Control characters are ascii codes 0-31 and 127. Not like the acute accent on an 'e'. – barlop Jul 10 '16 at 13:14
  • @CallumRogers Furthermore, as has been pointed out, latin vs non latin characters have absolutely nothing to do with this. "unicode combining characters" can occur both in latin, and other languages that aren't based on latin. – barlop Jul 10 '16 at 13:19
  • 1
    Can user the LINQ for shorter: return new string(s.Reverse().ToArray()); – zquanghoangz Aug 17 '16 at 05:22
  • This implementation is not Unicode safe. It will work, but could throw exceptions for some unicode characters (suplementary set) – Jim Nov 02 '17 at 16:30
  • This is not correct. It breaks when given strings with UTF-16 surrogate pairs. – Functino Apr 06 '18 at 01:05
  • I revoked the [4th revision](https://stackoverflow.com/revisions/228060/4) because it's not a succinct version of the original implementation, but an entirely different implementation. – Theodor Zoulias Nov 15 '20 at 15:13
  • not good for Unicode's chars!! Chinese, Greek etc. – ageroh Apr 27 '22 at 14:57
214

Here a solution that properly reverses the string "Les Mise\u0301rables" as "selbare\u0301siM seL". This should render just like selbarésiM seL, not selbaŕesiM seL (note the position of the accent), as would the result of most implementations based on code units (Array.Reverse, etc) or even code points (reversing with special care for surrogate pairs).

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

(And live running example here: https://ideone.com/DqAeMJ)

It simply uses the .NET API for grapheme cluster iteration, which has been there since ever, but a bit "hidden" from view, it seems.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • This fails for some locale dependent stuff, though. – R. Martinho Fernandes Feb 27 '13 at 12:46
  • Mmm. I'm guessing it's still future proof then (assuming it's a limitation of the BCL implementation? Fixes to that will automatically benefit use of these APIs) – sehe Feb 27 '13 at 13:06
  • 2
    It's actually significantly faster to instantiate StringInfo(s), then iterate through SubstringByTextElements(x, 1) and build a new string with a StringBuilder. –  Jul 09 '16 at 04:03
  • 4
    It's a bit strange that you've used Jon Skeet's example that he gave years earlier https://codeblog.jonskeet.uk/2009/11/02/omg-ponies-aka-humanity-epic-fail/ Les Misérables (though Jon didn't mention a solution, he just listed issues). Good that you came up with a solution. Maybe Jon skeet invented a time machine, went back to 2009 and posted the problem example that you used in your solution. – barlop Jul 10 '16 at 11:55
  • Also, you can make your example much much simpler if you say "lés" (it's 3 letters, and one e, and it's easy to see what's going on). instead of including the word miserables too, as somebody might look at the 'e' there in "miserables" by accident, which also has an 's' on it and on the other side. – barlop Jul 10 '16 at 13:11
  • As a sidenote, even this code doesn't support composite emojis, like ‍‍‍ and . – xanatos Jun 07 '18 at 07:00
  • @xanatos that's a bug you can file, because, yes, it should work for those too. Sounds like the implementation is stuck in an old Unicode version's semantics. – R. Martinho Fernandes Jun 08 '18 at 20:39
  • @R.MartinhoFernandes It is already on [coreclr issues](https://github.com/dotnet/corefx/issues/28416) – xanatos Jun 08 '18 at 22:04
  • 1
    Support for composite emojis was added with .NET 5 (https://learn.microsoft.com/en-us/dotnet/core/compatibility/3.1-5.0#stringinfo-and-textelementenumerator-are-now-uax29-compliant) – ckuri Nov 13 '20 at 16:12
131

This is turning out to be a surprisingly tricky question.

I would recommend using Array.Reverse for most cases as it is coded natively and it is very simple to maintain and understand.

It seems to outperform StringBuilder in all the cases I tested.

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

There is a second approach that can be faster for certain string lengths which uses 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);
    }

Note If you want to support the full Unicode UTF16 charset read this. And use the implementation there instead. It can be further optimized by using one of the above algorithms and running through the string to clean it up after the chars are reversed.

Here is a performance comparison between the StringBuilder, Array.Reverse and Xor method.

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

Here are the results:

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.

It seems that Xor can be faster for short strings.

Community
  • 1
  • 1
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506
  • 3
    That doesn't return a string - you need to wrap this in a call to "new String(...)" – Greg Beech Oct 23 '08 at 00:43
  • BTW .. I just had a look at the implementation of Array.Reverse, and its done naitively for chars ... it should be much faster than the StringBuilder option. – Sam Saffron Oct 23 '08 at 00:46
  • Good post, I think I would stick with my answer Array.Reverse not just because it seems to have good performance across string lengths but also because it is concise in the code. lets face it maintenance is half the problem. Also whats the performance penalty of all those using statements. – PeteT Oct 23 '08 at 02:52
  • 10
    These methods don't handle strings containing characters outside of the Base Multilingual Plane, i.e., Unicode characters >= U+10000 that are represented with two C# chars. I've posted an answer that handles such strings correctly. – Bradley Grainger Oct 23 '08 at 03:42
87

If you can use LINQ (.NET Framework 3.5+) than following one liner will give you short code. Don't forget to add using System.Linq; to have access to Enumerable.Reverse:

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

Notes:

  • not the fastest version - according to Martin Niederl 5.7 times slower than the fastest choice here.
  • this code as many other options completely ignores all sorts of multi-character combinations, so limit usage to homework assignments and strings which do not contain such characters. See another answer in this question for implementation that correctly handles such combinations.
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
SGRao
  • 1,055
  • 8
  • 6
  • That is around 5.7 times slower than the most upvoted version so i would not recommend using this! – Martin Niederl May 06 '17 at 13:42
  • When I wrote the original question in 2008 the system I was working on at that time was using C# 2.0 and LINQ wasn't available to me - as per the comment at the beginning of the question. – Guy Jul 11 '21 at 14:03
52

If the string contains Unicode data (strictly speaking, non-BMP characters) the other methods that have been posted will corrupt it, because you cannot swap the order of high and low surrogate code units when reversing the string. (More information about this can be found on my blog.)

The following code sample will correctly reverse a string that contains non-BMP characters, e.g., "\U00010380\U00010381" (Ugaritic Letter Alpa, Ugaritic 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);
}
Bradley Grainger
  • 27,458
  • 4
  • 91
  • 108
  • chars in C# are not bytes, they're actual characters. Thus, all of this is totally unnecessary. – Nick Johnson Oct 23 '08 at 13:20
  • 30
    Actually, chars in C# are 16-bit UTF-16 code units; a supplementary character is encoded using two of them, so this is necessary, – Bradley Grainger Oct 23 '08 at 15:14
  • For details, see §9.4.1.2 of the C# Language Specification: http://www.jaggersoft.com/csharp_standard/9.4.1.htm – Bradley Grainger Oct 23 '08 at 15:17
  • 15
    It seems like System.String really ought to expose a HereBeDragons property for strings that contain Unicode supplementary characters. – Robert Rossney Oct 23 '08 at 20:54
  • While your solution is better than the others, it does not take combining characters into account. – Sebastian Negraszus Nov 06 '12 at 13:15
  • 4
    @SebastianNegraszus: That's correct: this method just reverses the codepoints in the string. Reversing the [grapheme clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) would probably be more "useful" overall (but what's the "use" of reversing an arbitrary string in the first place?), but is not easy to implement with just the built-in methods in the .NET Framework. – Bradley Grainger Nov 06 '12 at 14:38
  • To detect grapheme clusters [`CharUnicodeInfo.GetUnicodeCategory`](http://msdn.microsoft.com/en-us/library/h6sx68ke.aspx) gives you most of the info you need (notably detecting combining code points). It will also tell you if the code point is a surrogate, so you don't need to hardcode the code point ranges :-) – Richard Feb 05 '13 at 09:34
  • 2
    @Richard: The rules for breaking grapheme clusters are a little more complicated than just detecting combining code points; see the documentation on [Grapheme Cluster Boundaries](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) in UAX #29 for more information. – Bradley Grainger Feb 05 '13 at 16:56
  • 1
    Very good info! Does **ANYONE** have a failing test for the Array.Reverse test? And by test I mean a sample string not a whole unit test... It would really help me (and others) convince different persons about this issue.. – Andrei Rînea Jul 02 '13 at 15:44
  • 1
    @Andrei try `"Les Mise\u0301rables"`. – R. Martinho Fernandes Oct 28 '13 at 14:58
  • 1
    A good and safe alternative to this approach is to use [`StringInfo.GetTextElementEnumerator`](https://msdn.microsoft.com/en-us/library/x2f3k4f6%28v=vs.110%29.aspx), which will automatically take care of surrogate pairs. To also take care of combining characters (of which there can be multiple in a row and each can itself be a surrogate pair), use `UnicodeCategory.NonSpacingMark`, `UnicodeCategory.SpacingCombiningMark` and `UnicodeCategory.EnclosingMark`. Also note that `UnicodeCategory.ModifierSymbol` can be used for the _surrounding_ characters. Reversing a string is truly _not trivial_. – Abel May 05 '15 at 15:48
31

Ok, in the interest of "don't repeat yourself," I offer the following solution:

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

My understanding is that this implementation, available by default in VB.NET, properly handles Unicode characters.

richardtallent
  • 34,724
  • 14
  • 83
  • 123
21

Have a look at the wikipedia entry here. They implement the String.Reverse extension method. This allows you to write code like this:

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

They also use the ToCharArray/Reverse combination that other answers to this question suggest. The source code looks like this:

public static string Reverse(this string input)
{
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}
Mike Thompson
  • 6,708
  • 3
  • 32
  • 39
21

Starting with .NET Core 2.1 there is a new way to reverse a string using the string.Create method.

Note that this solution does not handle Unicode combining characters etc. correctly, as "Les Mise\u0301rables" would be converted to "selbarésiM seL". See the other answers for a better solution.

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

This essentially copies the characters of input to a new string and reverses the new string in-place.

Why is string.Create useful?

When we create a string from an existing array, a new internal array is allocated and the values are copied. Otherwise, it would be possible to mutate a string after its creation (in a safe environment). That is, in the following snippet we have to allocate an array of length 10 twice, one as the buffer and one as the string's internal array.

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

string.Create essentially allows us to manipulate the internal array during creation time of the string. This is, we do not need a buffer anymore and can therefore avoid allocating that one char array.

Steve Gordon has written about it in more detail here. There is also an article on MSDN.

How to use string.Create?

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

The method takes three parameters:

  1. The length of the string to create,
  2. the data you want to use to dynamically create the new string,
  3. and a delegate that creates the final string from the data, where the first parameter points to the internal char array of the new string and the second is the data (state) you passed to string.Create.

Inside the delegate we can specify how the new string is created from the data. In our case, we just copy the characters of the input string to the Span used by the new string. Then we reverse the Span and hence the whole string is reversed.

Benchmarks

To compare my proposed way of reversing a string with the accepted answer, I have written two benchmarks using 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);
}

Here are the results on my machine:

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

As you can see, with ReverseWithStringCreate we allocate only half of the memory used by the ReverseWithArray method.

Flogex
  • 584
  • 6
  • 15
  • It is much faster than Linq reverse – code4j Oct 07 '19 at 17:58
  • I just tried this method on .Net 6 and it returns "selbaŕesiM seL" which seems correct, while the other mentioned Grapheme-method returns "selbarésiM seL" which is incorrect. So it seems that this method is not only correct, but around 100x faster. – wertzui Mar 02 '22 at 18:46
  • This is the fastest way to reverse a string I have found yet. It's also so simple and readable. – Thanos Kyprianos Aug 16 '22 at 11:28
18

Greg Beech posted an unsafe option that is indeed as fast as it gets (it's an in-place reversal); but, as he indicated in his answer, it's a completely disastrous idea.

That said, I'm surprised there is so much of a consensus that Array.Reverse is the fastest method. There's still an unsafe approach that returns a reversed copy of a string (no in-place reversal shenanigans) significantly faster than the Array.Reverse method for small strings:

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

Here are some benchmark results.

You can see that the performance gain shrinks and then disappears against the Array.Reverse method as the strings get larger. For small- to medium-sized strings, though, it's tough to beat this method.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
16

The easy and nice answer is using the Extension Method:

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

and here's the output:

string Answer = "12345".Inverse(); // = "54321"
Mehdi Khademloo
  • 2,754
  • 2
  • 20
  • 40
15

If you want to play a really dangerous game, then this is by far the fastest way there is (around four times faster than the Array.Reverse method). It's an in-place reverse using pointers.

Note that I really do not recommend this for any use, ever (have a look here for some reasons why you should not use this method), but it's just interesting to see that it can be done, and that strings aren't really immutable once you turn on unsafe code.

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;
    }
}
Community
  • 1
  • 1
Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • 1
    Im pretty sure this will return incorrect results for utf16 strings, it is really asking trouble :) – Sam Saffron Oct 23 '08 at 03:07
  • 1
    Hi you should link to this post on this http://stackoverflow.com/questions/229346/why-should-i-never-use-an-unsafe-block-to-modify-a-string , as I said before this is really asking for trouble ... – Sam Saffron Oct 23 '08 at 11:53
  • 1
    This may be completely evil and ill-advised (as you yourself concede), but there's still a high-performance way to reverse a string using `unsafe` code that *isn't* evil and *still* beats `Array.Reverse` in many cases. Take a look at my answer. – Dan Tao Jun 15 '10 at 18:33
14

Firstly you don't need to call ToCharArray as a string can already be indexed as a char array, so this will save you an allocation.

The next optimisation is to use a StringBuilder to prevent unnecessary allocations (as strings are immutable, concatenating them makes a copy of the string each time). To further optimise this we pre-set the length of the StringBuilder so it won't need to expand its buffer.

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

Edit: Performance Data

I tested this function and the function using Array.Reverse with the following simple program, where Reverse1 is one function and Reverse2 is the other:

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

It turns out that for short strings the Array.Reverse method is around twice as quick as the one above, and for longer strings the difference is even more pronounced. So given that the Array.Reverse method is both simpler and faster I'd recommend you use that rather than this one. I leave this one up here just to show that it isn't the way you should do it (much to my surprise!)

Greg Beech
  • 133,383
  • 43
  • 204
  • 250
12

Try using Array.Reverse


public string Reverse(string str)
{
    char[] array = str.ToCharArray();
    Array.Reverse(array);
    return new string(array);
}
Mike Two
  • 44,935
  • 9
  • 80
  • 96
  • Fails to handle combining code points amongst many other things. – Mooing Duck Mar 01 '13 at 23:16
  • @MooingDuck I looked up code points. Yes. You are correct. It does not handle code points. It is tough to determine all of the requirements for such a simple looking question. Thanks for the feedback – Mike Two Mar 02 '13 at 01:28
11

"Best" can depend on many things, but here are few more short alternatives ordered from fast to slow:

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̽"  
Slai
  • 22,144
  • 5
  • 45
  • 53
10
public static string Reverse(string input)
{
    return string.Concat(Enumerable.Reverse(input));
}

Of course you can extend string class with Reverse method

public static class StringExtensions
{
    public static string Reverse(this string input)
    {
        return string.Concat(Enumerable.Reverse(input));
    }
}
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
7

Don't bother with a function, just do it in place. Note: The second line will throw an argument exception in the Immediate window of some VS versions.

string s = "Blah";
s = new string(s.ToCharArray().Reverse().ToArray()); 
B H
  • 1,730
  • 18
  • 24
7

Sorry for long post, but this might be interesting

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

Results:

  • ReverseUsingCharacterBuffer: 346ms
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • ReverseUsingXOR: 319ms
aku
  • 122,288
  • 32
  • 173
  • 203
  • I added a similar comparison in my post, its a community wiki so you should be able to edit. The performance really depends on the length of the string as well as the algorithm, it would be interesting to graph it. I still think Array.Reverse will be fastest in all cases ... – Sam Saffron Oct 23 '08 at 01:43
  • "will be fastest in all cases" when magical TrySZReverse function (it's used in Reverse implementation) fails, Array.Reverse fallbacks to simple implementation involving boxing, so my method will win. However I don't know what is a condition to make TrySZReverse fail. – aku Oct 23 '08 at 02:03
  • Turns out its not fastest in all cases :), I updated my post. This still needs to be tested with unicode for both correctness and speed. – Sam Saffron Oct 23 '08 at 02:49
6
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();
    }
}

Output

For size: 10

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

For size: 100

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

For size: 1000

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

For size: 10000

Naive Ticks: 32
Efficient linear Ticks: 28
Recursive Ticks: 84808
DotNet Reverse Ticks: 33
Marcel Valdez Orozco
  • 2,985
  • 1
  • 25
  • 24
  • 1
    Need to check for empty string in `Reverse(...)`. Otherwise, good work. –  Jan 06 '20 at 21:37
5

Simplest way:

string reversed = new string(text.Reverse().ToArray());
Shadi Serhan
  • 309
  • 3
  • 9
4

Stack-based solution.

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

Or

    public static string Reverse(string text)
    {
        var stack = new Stack<char>(text);
        return string.Join("", stack);
    }
Rezo Megrelidze
  • 2,877
  • 1
  • 26
  • 29
4

"Better way" depends on what is more important to you in your situation, performance, elegance, maintainability etc.

Anyway, here's an approach using Array.Reverse:

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

string reversed = new string(charArray);
Ash
  • 60,973
  • 31
  • 151
  • 169
4

Had to submit a recursive example:

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));
}
Abel
  • 56,041
  • 24
  • 146
  • 247
JPrescottSanders
  • 2,151
  • 2
  • 16
  • 24
4

I've made a C# port from Microsoft.VisualBasic.Strings. I'm not sure why they keep such useful functions (from VB) outside the System.String in Framework, but still under Microsoft.VisualBasic. Same scenario for financial functions (e.g. 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();
}
natenho
  • 5,231
  • 4
  • 27
  • 52
  • +1, a nice addition! I just tried it with `string s = "abo\u0327\u0307\u035d\U0001d166cd"`, which contains the letter `o` followed by 3 combining diacritical marks in the BMP and one combining mark (MUSICAL SYMBOL COMBINING STEM) from the astral plane (non-BMP) and it keeps them intact. But the method is slow if such characters only appear at the end of a long string, as it has to go twice over the whole array. – Abel May 06 '15 at 12:44
4

How about:

    private string Reverse(string stringToReverse)
    {
        char[] rev = stringToReverse.Reverse().ToArray();
        return new string(rev); 
    }
Juliet
  • 80,494
  • 45
  • 196
  • 228
Zamir
  • 77
  • 3
  • Has the same codepoint issues as other methods above and will perform much slower than when doing a `ToCharArray` first. The LINQ enumerator is also way slower than `Array.Reverse()`. – Abel May 06 '15 at 10:44
4

Since I like a couple of the answers - one for using string.Create and therefore high performance and low allocation and another for correctness - using the StringInfo class, I decided a combined approach is needed. This is the ultimate string reversal method :)

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

There's an even better way using a method of the StringInfo class which skips a lot of string allocations by the Enumerator by returning just the indexes.

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

Some benchmarks compared to the LINQ solution:

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
SET
  • 158
  • 1
  • 3
  • 9
3

Sorry for posting on this old thread. I am practicing some code for an interview.

This was what I came up with for C#. My first version before refactoring was horrible.

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

In Contrast to the Array.Reverse method below, it appears faster with 12 characters or less in the string. After 13 characters, the Array.Reverse starts to get faster, and it eventually dominates pretty heavily on speed. I just wanted to point out approximately where the speed starts to change.

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

    Array.Reverse(charA);

    return new String(charA);
}

At 100 characters in the string, it is faster than my version x 4. However, if I knew that the strings would always be less than 13 characters, I would use the one I made.

Testing was done with Stopwatch and 5000000 iterations. Also, I'm not sure if my version handles Surrogates or combined character situations with Unicode encoding.

Jason Ausborn
  • 317
  • 2
  • 8
3

If it ever came up in an interview and you were told you can't use Array.Reverse, i think this might be one of the fastest. It does not create new strings and iterates only over half of the array (i.e O(n/2) iterations)

    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);
    }
mike01010
  • 5,226
  • 6
  • 44
  • 77
  • 3
    I'm pretty certain stringToReverse.ToCharArray() call will produce a O(N) execution time. – Marcel Valdez Orozco Sep 08 '12 at 05:35
  • 1
    In [Big-O notation](http://en.wikipedia.org/wiki/Big_O_notation), the factor not dependent on `x`, or in your case, `n`, is not used. Your algorithm has performance `f(x) = x + ½x + C`, where C is some constant. Since both `C` and the factor `1½` are not dependent on `x`, your algorithm is `O(x)`. That does not mean that it won't be faster for any input of length `x`, but its performance is linearly dependent on the input length. To answer @MarcelValdezOrozco, yes, it is also `O(n)`, though it copies per 16-byte chunks to improve speed (it does not use a straight `memcpy` on the total length). – Abel May 06 '15 at 12:11
2
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);
        }
antyrat
  • 27,479
  • 9
  • 75
  • 76
Shrini
  • 29
  • 1
2

If you have a string that only contains ASCII characters, you can use this method.

    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);
    }
Rezo Megrelidze
  • 2,877
  • 1
  • 26
  • 29
2

First of all what you have to understand is that str+= will resize your string memory to make space for 1 extra char. This is fine, but if you have, say, a book with 1000 pages that you want to reverse, this will take very long to execute.

The solution that some people might suggest is using StringBuilder. What string builder does when you perform a += is that it allocates much larger chunks of memory to hold the new character so that it does not need to do a reallocation every time you add a char.

If you really want a fast and minimal solution I'd suggest the following:

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

In this solution there is one initial memory allocation when the char[] is initialized and one allocation when the string constructor builds the string from the char array.

On my system I ran a test for you that reverses a string of 2 750 000 characters. Here are the results for 10 executions:

StringBuilder: 190K - 200K ticks

Char Array: 130K - 160K ticks

I also ran a test for normal String += but I abandoned it after 10 minutes with no output.

However, I also noticed that for smaller strings the StringBuilder is faster, so you will have to decide on the implementation based on the input.

Cheers

Reasurria
  • 1,808
  • 16
  • 14
2
public static string reverse(string s) 
{
    string r = "";
    for (int i = s.Length; i > 0; i--) r += s[i - 1];
    return r;
}
ddagsan
  • 1,786
  • 18
  • 21
2

If someone asks about string reverse, the intension could be to find out whether you know any bitwise operation like XOR. In C# you have Array.Reverse function, however, you can do using simple XOR operation in few lines of code(minimal)

    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);
    }
Ramakrishna Talla
  • 1,011
  • 12
  • 7
2

There are a few correct answers where StringInfo.GetTextElementEnumerator() is being used. Kudos to you!

Now, let's find the most efficient way to use this method. First, most answers involve calling Reverse() and ToArray() which is a big no-no on hot paths. For optimal performance, we want to avoid allocating garbage. E.g. temporary strings, allocators, arrays etc.

Optimized reversal of string

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

Note that the GetTextElementEnumerator() API contains a bug in .NET Core 3.1 and earlier. Make sure to run .NET 5 or later! Finally, make sure to check out issue #19423 where API improvements are being discussed.

l33t
  • 18,692
  • 16
  • 103
  • 180
1
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;
        }

Faster than above method

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);
        }
vikas
  • 2,780
  • 4
  • 27
  • 37
1
    string original = "Stack Overflow";
    string reversed = new string(original.Reverse().ToArray());
1

There are various ways to reverse the string, I have shown 3 of them below.

-- Using Array.Reverse function.

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

-- using string only

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

-- Using only char array

 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);
    }
Munavvar
  • 802
  • 1
  • 11
  • 33
1

As simple as this:

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

See the output.

Raktim Biswas
  • 4,011
  • 5
  • 27
  • 32
  • 2
    Please be aware, that using this method you're creating `x.Length` times a new `string` object `x1` as you're not taking into account the inherent immutability of `string`. – Wim Ombelets Jul 31 '17 at 10:11
1

here is a unicode safe version of the function, written as an extension that will safely handle unicode. It is close to the marked complete answer, but will not throw exceptions for "Invalid high surrogate character".

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

    }
}
Jim
  • 864
  • 1
  • 8
  • 16
1

How about use 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;
}
joegreentea
  • 325
  • 3
  • 12
1

Using LINQ's Aggregate function

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

Handles all type of unicode characters

using 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();
    }
Gigabyte
  • 71
  • 4
1

Another option using span reverse in System.Memory.

public static string Reverse(string str)
{
    Span<char> span = stackalloc char[str.Length];
    str.AsSpan().CopyTo(span);
    span.Reverse();
    return new String(span);
}
Magnus
  • 45,362
  • 8
  • 80
  • 118
0

I was asked a similar question in interview. This was my response, although it is probably not as fast in performance as other answers. My question was phrased as "Make a class that can have a method to print a string backwards":

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);
        }
    }
}
simon
  • 854
  • 1
  • 9
  • 23
0
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();
}
adiga
  • 34,372
  • 9
  • 61
  • 83
Deep
  • 54
  • 5
0
     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();
        }
    }

  }
0

We can use two pointers one pointing to the start of the string and another to the end of the string. Then swap both ith and jth values each time and increment ith pointer +1 and decrement jth pointer -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
Perdente
  • 75
  • 2
  • 11
-1
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;
    }
}
Rezo Megrelidze
  • 2,877
  • 1
  • 26
  • 29
AMIN
  • 1
-1

May be using .NET 6.0 or .NET 7.0 it is shortst way

string name = Console.ReadLine();
Console.WriteLine(string.Join("", name.Reverse().ToArray()));
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
Veselin Stoychev
  • 59
  • 1
  • 1
  • 5
  • It is not the shortest way. And why introduce any `Console` operations when the OP didn't include them? `string.Concat(name.Reverse())` is much shorter and cleaner. – Enigmativity Jul 05 '23 at 12:40
  • Using Linq `Reverse()` extension method on `IEnumerable` has already been proposed in other answers. – Kai Thoma Jul 11 '23 at 07:05
-2

This is the code used for reverse string

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;
}
Neels
  • 2,547
  • 6
  • 33
  • 40
Milad Jafari
  • 970
  • 10
  • 15
-3

It's very simple

static void Reverse()
    {
        string str = "PankajRawat";
        var arr = str.ToCharArray();
        for (int i = str.Length-1; i >= 0; i--)
        {
            Console.Write(arr[i]);
        }
    }
Pankaj Rawat
  • 4,037
  • 6
  • 41
  • 73
-4
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));
}
takrl
  • 6,356
  • 3
  • 60
  • 69
Saeed
  • 1
  • It's does it's job, but it's probably the worst way to reverse a string because of String obj **MUCH** memory allocations and recursion. – Vinigas Jan 08 '19 at 13:47
-5

Reverse a String without even using a new string. Lets say

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--];
}
Mark Henry
  • 255
  • 3
  • 8
-9

SELECT REVERSE('somestring'); Done.