web-dev-qa-db-fra.com

Meilleur moyen d'inverser une chaîne

Je viens juste de devoir écrire une fonction d'inverse de chaîne en C # 2.0 (c'est-à-dire que LINQ n'est pas disponible) et j'ai trouvé ceci:

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

Personnellement, je ne suis pas folle de la fonction et je suis convaincue qu'il existe un meilleur moyen de le faire. Y a-t-il?

403
Guy
public static string Reverse( string s )
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}
533
PeteT

Voici une solution qui inverse correctement la chaîne "Les Mise\u0301rables" en tant que "selbare\u0301siM seL". Cela devrait être identique à selbarésiM seL, pas selbaŕesiM seL (notez la position de l'accent), comme le ferait la plupart des implémentations basées sur des unités de code (Array.Reverse, etc.) ou même des points de code (inverser avec un soin particulier pour les paires de substitution).

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

(Et exemple en direct ici: https://ideone.com/DqAeMJ )

Il utilise simplement le .NET API pour l'itération de grappe de graphèmes , qui existe depuis toujours, mais un peu "caché", semble-t-il.

167
R. Martinho Fernandes

Cela s'avère être une question étonnamment délicate.

Je recommanderais d'utiliser Array.Reverse dans la plupart des cas, car il est codé de manière native et il est très simple à gérer et à comprendre.

Il semble surperformer StringBuilder dans tous les cas que j'ai testés.

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

Il existe une deuxième approche qui peut être plus rapide pour certaines longueurs de chaîne qui tilise 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);
    }

Remarque Si vous souhaitez prendre en charge le jeu de caractères Unicode UTF16 complet lisez ceci . Et utilisez la mise en œuvre là-bas à la place. Il peut être optimisé davantage en utilisant l’un des algorithmes ci-dessus et en parcourant la chaîne pour le nettoyer après l’inversion des caractères.

Voici une comparaison des performances entre les méthodes StringBuilder, Array.Reverse et Xor.

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

Voici les résultats:

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.

Il semble que Xor puisse être plus rapide pour les chaînes courtes.

125
Sam Saffron

Si la chaîne contient des données Unicode (à proprement parler, des caractères autres que BMP), les autres méthodes publiées la corrompront, car vous ne pouvez pas échanger l'ordre des unités de code de substitution haute et basse lors de l'inversion de la chaîne. (Plus d'informations à ce sujet peuvent être trouvées sur mon blog .)

L'exemple de code suivant inversera correctement une chaîne contenant des caractères non-BMP, par exemple "\ U00010380\U00010381" (lettre ugaritique, lettre bêta ougaritique).

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);
}
49
Bradley Grainger

D'en haut 3.5 Framework

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

Ok, dans l'intérêt de "ne vous répétez pas", j'offre la solution suivante:

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

Si j'ai bien compris, cette implémentation, disponible par défaut dans VB.NET, gère correctement les caractères Unicode.

23
richardtallent

Greg Beech a posté une option unsafe qui est en effet aussi rapide que possible (c'est un renversement sur place); mais, comme il l’a indiqué dans sa réponse, c’est ne idée complètement désastreuse.

Cela dit, je suis surpris qu'il y ait un si grand consensus que Array.Reverse est la méthode la plus rapide. Il existe toujours une approche unsafe qui renvoie une copie inversée d'une chaîne (pas de shenanigans d'inversion sur place) nettement plus rapidement que la méthode Array.Reverse pour les petites chaînes:

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

Voici quelques résultats de référence .

Vous pouvez constater que le gain en performances diminue et disparaît ensuite avec la méthode Array.Reverse à mesure que les chaînes grossissent. Pour les cordes de taille petite à moyenne, cependant, il est difficile de battre cette méthode.

17
Dan Tao

La réponse facile et agréable utilise la méthode d'extension:

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

et voici la sortie:

string Answer = "12345".Inverse(); // = "54321"
14
Mehdi Khademloo

Si vous voulez jouer à un jeu vraiment dangereux, alors c'est de loin le moyen le plus rapide (environ quatre fois plus rapide que la méthode Array.Reverse.). C'est un retour sur place à l'aide de pointeurs.

Notez que je ne le recommande vraiment pas pour une utilisation, jamais ( regardez ici pour certaines raisons pour lesquelles vous ne devriez pas utiliser cette méthode ), mais il est juste intéressant de voir que cela peut être fait, et que les chaînes ne sont pas vraiment immuables une fois que vous activez un code non sécurisé.

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;
    }
}
13
Greg Beech

Jetez un coup d'œil à l'entrée wikipedia ici . Ils implémentent la méthode d'extension String.Reverse. Cela vous permet d'écrire du code comme ceci:

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

Ils utilisent également la combinaison ToCharArray/Reverse suggérée par d’autres réponses à cette question. Le code source ressemble à ceci:

public static string Reverse(this string input)
{
    char[] chars = input.ToCharArray();
    Array.Reverse(chars);
    return new String(chars);
}
11
Mike Thompson

Premièrement, vous n'avez pas besoin d'appeler ToCharArray, car une chaîne peut déjà être indexée sous forme de tableau de caractères, ce qui vous évitera une allocation.

L'optimisation suivante consiste à utiliser un StringBuilder pour éviter les attributions inutiles (les chaînes étant immuables, leur concaténation crée une copie de la chaîne à chaque fois). Pour optimiser davantage cela, nous pré-définissons la longueur de la variable StringBuilder afin de ne pas avoir à agrandir son tampon.

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

J'ai testé cette fonction et la fonction utilisant Array.Reverse avec le programme simple suivant, où Reverse1 est une fonction et Reverse2 est l'autre:

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

Il s'avère que pour les chaînes courtes, la méthode Array.Reverse est environ deux fois plus rapide que celle ci-dessus, et pour les chaînes plus longues, la différence est encore plus prononcée. Donc, étant donné que la méthode Array.Reverse est à la fois plus simple et plus rapide, je vous recommande de l’utiliser plutôt que celle-ci. Je laisse celui-ci ici juste pour montrer que ce n'est pas la façon dont vous devriez le faire (à ma grande surprise!)

11
Greg Beech
public static string Reverse(string input)
{
    return string.Concat(Enumerable.Reverse(input));
}

Bien sûr, vous pouvez étendre la classe string avec la méthode Reverse

public static class StringExtensions
{
    public static string Reverse(this string input)
    {
        return string.Concat(Enumerable.Reverse(input));
    }
}
10
Vlad Bezden

Essayez d'utiliser Array.Reverse


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

Ne vous embêtez pas avec une fonction, faites-la simplement. Remarque: la deuxième ligne lève une exception d'argument dans la fenêtre Immédiate de certaines versions de VS.

string s = "Blah";
s = new string(s.ToCharArray().Reverse().ToArray()); 
6
B H

Désolé pour long post, mais cela pourrait être intéressant

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

Résultats:

  • ReverseUsingCharacterBuffer: 346ms
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • ReverseUsingXOR: 319ms
5
aku

"Meilleur" peut dépendre de beaucoup de choses, mais voici quelques alternatives plus courtes ordonnées du plus rapide au plus lent:

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̽"  ????
5
Slai
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();
    }
}

sortie

pour la taille: 1

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

pour la taille: 1

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

pour la taille: 10

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

Pour la taille: 100

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

Solution basée sur la pile.

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

Ou

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

Manière la plus simple:

string reversed = new string(text.Reverse().ToArray());
4
Shady Sirhan

Devait soumettre un exemple récursif:

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

J'ai créé un port C # à partir de Microsoft.VisualBasic.Strings . Je ne sais pas pourquoi ils gardent de telles fonctions utiles (de VB) en dehors de System.String dans Framework, mais toujours sous Microsoft.VisualBasic. Même scénario pour les fonctions financières (par exemple, 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();
}
3
natenho

À partir de .NET Core 2.1, il existe un nouveau moyen d'inverser une chaîne à l'aide de la méthode string.Create.

Notez que cette solution ne gère pas correctement la combinaison de caractères Unicode, etc., car "Les Mise\u00301rables" serait converti en "selbarésiM seL". Les les autres réponses pour une meilleure solution.

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

Cela copie essentiellement les caractères de input dans une nouvelle chaîne et inverse la nouvelle chaîne sur place.

Pourquoi string.Create est-il utile?

Lorsque nous créons une chaîne à partir d'un tableau existant, un nouveau tableau interne est alloué et les valeurs sont copiées. Sinon, il serait possible de muter une chaîne après sa création (dans un environnement sécurisé). C'est-à-dire que dans l'extrait suivant, nous devons allouer un tableau de longueur 10 deux fois, l'un en tant que tampon et l'autre en tant que tableau interne de la chaîne.

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

string.Create nous permet essentiellement de manipuler le tableau interne pendant la création de la chaîne. En d’autres termes, nous n’avons plus besoin d’un tampon et pouvons donc éviter d’allouer ce tableau de caractères.

Steve Gordon en a écrit plus en détail ici . Il y a aussi un article sur MSDN .

Comment utiliser string.Create?

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

La méthode prend trois paramètres:

  1. La longueur de la chaîne à créer,
  2. les données que vous souhaitez utiliser pour créer dynamiquement la nouvelle chaîne,
  3. et un délégué qui crée la dernière chaîne à partir des données, où le premier paramètre pointe vers le tableau interne char de la nouvelle chaîne et le second est la donnée (état) que vous avez transmise à string.Create.

Dans le délégué, nous pouvons spécifier comment la nouvelle chaîne est créée à partir des données. Dans notre cas, nous copions simplement les caractères de la chaîne d'entrée dans le Span utilisé par la nouvelle chaîne. Ensuite, nous inversons la Span et par conséquent toute la chaîne est inversée.

Des repères

Pour comparer la méthode proposée pour inverser une chaîne de caractères avec la réponse acceptée, j'ai écrit deux tests en utilisant 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);
}

Voici les résultats sur ma 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 |

Comme vous pouvez le constater, avec ReverseWithStringCreate, nous n’allouons que la moitié de la mémoire utilisée par la méthode ReverseWithArray.

3
Flogex

Que diriez-vous:

    private string Reverse(string stringToReverse)
    {
        char[] rev = stringToReverse.Reverse().ToArray();
        return new string(rev); 
    }
3
Zamir

Désolé pour poster sur ce vieux fil. Je pratique du code pour une interview.

C’est ce que j’ai trouvé pour C #. Ma première version avant refactoring était 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);
}

Contrairement à la méthode Array.Reverse ci-dessous, elle apparaît plus rapidement si la chaîne contient 12 caractères ou moins. Après 13 caractères, le Array.Reverse commence à aller plus vite et finit par dominer assez lourdement la vitesse. Je voulais juste indiquer approximativement où la vitesse commence à changer.

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

    Array.Reverse(charA);

    return new String(charA);
}

A 100 caractères dans la chaîne, c'est plus rapide que ma version x 4. Cependant, si je savais que les chaînes auraient toujours moins de 13 caractères, j'utiliserais celle que j'ai créée.

Les tests ont été effectués avec Stopwatch et 5000000 itérations. De plus, je ne sais pas si ma version gère les situations de substitution ou de combinaison de caractères avec le codage Unicode.

3
Jason Ausborn

Si vous avez une chaîne ne contenant que des caractères ASCII, vous pouvez utiliser cette méthode.

    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);
    }
2
Raz Megrelidze

Si cela se produisait dans une interview et que l'on vous disait que vous ne pouvez pas utiliser Array.Reverse, je pense que cela pourrait être l'un des plus rapides. Il ne crée pas de nouvelles chaînes et n'itère que plus de la moitié du tableau (c'est-à-dire O(n/2) itérations)

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

"Meilleure façon" dépend de ce qui est le plus important pour vous dans votre situation, performances, élégance, facilité de maintenance, etc.

Quoi qu'il en soit, voici une approche utilisant 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);
2
Ash
public static string reverse(string s) 
{
    string r = "";
    for (int i = s.Length; i > 0; i--) r += s[i - 1];
    return r;
}
2
ddagsan

Comment utiliser 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;
}
1
joegreentea

Étant donné que j'aime quelques réponses - une pour utiliser string.Create et donc des performances élevées et une faible allocation et une autre pour l'exactitude - en utilisant la classe StringInfo, j'ai décidé qu'une approche combinée était nécessaire. C'est la méthode ultime d'inversion de chaîne :)

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

Il existe un moyen encore meilleur d'utiliser une méthode de la classe StringInfo qui ignore beaucoup d'allocations de chaînes par l'énumérateur en renvoyant uniquement les index.

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

Quelques points de repère par rapport à la solution LINQ:

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

voici une version unicode de la fonction, écrite sous la forme d’une extension qui gérera en toute sécurité l’unicode. Il est proche de la réponse complète marquée, mais ne jettera pas des exceptions pour "Caractère de substitution élevé non valide".

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
Jim

Aussi simple que cela:

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

Voir le sortie .

1
Raktim Biswas
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;
        }

plus rapide que la méthode ci-dessus

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
vikas

Utilisation de la fonction Aggrégation de LINQ

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

Tout d’abord, vous devez comprendre que str + = redimensionnera la mémoire de votre chaîne pour faire de la place pour 1 caractère supplémentaire. C'est bien, mais si vous avez, par exemple, un livre de 1 000 pages que vous souhaitez inverser, l'exécution sera très longue.

La solution que certaines personnes pourraient suggérer utilise StringBuilder. Lorsque vous effectuez un signe + =, le constructeur de chaînes fait en sorte qu'il alloue des morceaux de mémoire beaucoup plus volumineux pour contenir le nouveau caractère, de sorte qu'il ne soit pas nécessaire de procéder à une réallocation à chaque fois que vous ajoutez un caractère.

Si vous voulez vraiment une solution rapide et minimale, je vous suggère ce qui suit:

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

Dans cette solution, il existe une allocation de mémoire initiale lorsque char [] est initialisée et une allocation lorsque le constructeur de chaîne construit la chaîne à partir du tableau char.

Sur mon système, j’ai exécuté pour vous un test qui inverse une chaîne de 2 750 000 caractères. Voici les résultats pour 10 exécutions:

StringBuilder: 190K - 200K ticks

Tableau de caractères: 130K - 160K ticks

J'ai aussi fait un test pour String + = normal mais je l'ai abandonné après 10 minutes sans sortie.

Cependant, j'ai également remarqué que pour les chaînes plus petites, StringBuilder est plus rapide. Vous devrez donc décider de l'implémentation en fonction de l'entrée.

À votre santé

1
Reasurria
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);
        }
1
Shrini
    string original = "Stack Overflow";
    string reversed = new string(original.Reverse().ToArray());
0
Raphael Saldanha
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();
}
0
Deep
     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
Punit Pandya

Il y a différentes façons d'inverser la chaîne, j'en ai montré 3 ci-dessous.

- Utilisation de la fonction Array.Reverse.

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

- en utilisant uniquement de la ficelle

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

- Utiliser uniquement un tableau de caractères

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

On m'a posé une question similaire en entrevue. C’est ma réponse, même si les performances ne sont probablement pas aussi rapides que les autres réponses. Ma question était libellée comme suit: "Créez une classe pouvant avoir une méthode pour imprimer une chaîne à l'envers":

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