Beste manier om een string om te draaien

Ik moest net een string-reverse-functie schrijven in C# 2.0 (d.w.z. LINQ niet beschikbaar) en kwam met dit:

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

Persoonlijk ben ik niet gek op de functie en ben ervan overtuigd dat er een betere manier is om het te doen. Is er?


Antwoord 1, autoriteit 100%

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

Antwoord 2, autoriteit 30%

Hier een oplossing die de string "Les Mise\u0301rables"correct omkeert als "selbare\u0301siM seL". Dit zou net als selbarésiM seLmoeten renderen, niet selbaŕesiM seL(let op de positie van het accent), zoals het resultaat zou zijn van de meeste implementaties op basis van code-eenheden (Array.Reverse, enz.) of zelfs codepunten (omgekeerd met speciale zorg voor surrogaatparen).

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

(En hier een live-voorbeeld: https://ideone.com/DqAeMJ)

Het gebruikt gewoon de .NET API voor grafeemclusteriteratie, die er sindsdien is ooit, maar een beetje “verborgen” uit het zicht, zo lijkt het.


Antwoord 3, autoriteit 19%

Dit blijkt een verrassend lastige vraag te zijn.

Ik raad aan om in de meeste gevallen Array.Reverse te gebruiken, omdat het native gecodeerd is en heel eenvoudig te onderhouden en te begrijpen is.

Het lijkt StringBuilder te overtreffen in alle gevallen die ik heb getest.

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

Er is een tweede benadering die sneller kan zijn voor bepaalde stringlengtes die gebruikt 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);
    }

OpmerkingAls u de volledige Unicode UTF16-tekenset wilt ondersteunen lees dit. En gebruik in plaats daarvan de implementatie daar. Het kan verder worden geoptimaliseerd door een van de bovenstaande algoritmen te gebruiken en door de tekenreeks te lopen om deze op te schonen nadat de tekens zijn omgekeerd.

Hier is een prestatievergelijking tussen de StringBuilder-, Array.Reverse- en Xor-methode.

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

Hier zijn de resultaten:

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.

Het lijkt erop dat Xor sneller kan zijn voor korte snaren.


Antwoord 4, autoriteit 10%

Als u LINQ (.NET Framework 3.5+) kunt gebruiken, krijgt u door het volgen van één voering een korte code. Vergeet niet using System.Linq;toe te voegen om toegang te krijgen tot Enumerable.Reverse:

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

Opmerkingen:

  • niet de snelste versie – volgens Martin Niederl5,7 keer langzamer dan de snelste keuze hier.
  • net als veel andere opties negeert deze code alle soorten combinaties van meerdere tekens, dus beperk het gebruik tot huiswerkopdrachten en tekenreeksen die geendergelijke tekens bevatten. Zie een ander antwoordin deze vraag voor een implementatie die dergelijke combinaties correct afhandelt.

Antwoord 5, autoriteit 7%

Als de tekenreeks Unicode-gegevens bevat (strikt genomen, niet-BMP-tekens), zullen de andere geposte methoden deze beschadigen, omdat u de volgorde van hoge en lage surrogaatcode-eenheden niet kunt verwisselen bij het omkeren van de tekenreeks. (Meer informatie hierover is te vinden op mijn blog.)

Het volgende codevoorbeeld zal een tekenreeks die niet-BMP-tekens bevat correct omkeren, bijvoorbeeld “\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);
}

Antwoord 6, autoriteit 4%

Ok, in het belang van “herhaal jezelf niet”, bied ik de volgende oplossing:

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

Ik heb begrepen dat deze implementatie, die standaard beschikbaar is in VB.NET, goed omgaat met Unicode-tekens.


Antwoord 7, autoriteit 3%

Greg Beech plaatste een unsafeoptie die inderdaad zo snel mogelijk is (het is een in-place omkering); maar, zoals hij in zijn antwoord aangaf, is het een volledig rampzalig idee.

Dat gezegd hebbende, verbaast het me dat er zoveel consensus is dat Array.Reversede snelste methode is. Er is nog steeds een unsafe-benadering die een omgekeerde kopie van een tekenreeks retourneert (geen in-place reversal shenanigans) aanzienlijk sneller dan de Array.Reverse-methodevoor kleine tekenreeksen:

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

Hier zijn enkele benchmarkresultaten .

U kunt zien dat de prestatieverstroom krimpt en vervolgens verdwijnt tegen de Array.Reversemethode zoals de snaren groter worden. Voor kleine tot middelgrote snaren is het echter moeilijk om deze methode te verslaan.


Antwoord 8, Autoriteit 3%

Het eenvoudige en mooie antwoord van de extensie:

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

en hier is de uitvoer:

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

Antwoord 9, Autoriteit 2%

Bekijk de Wikipedia-invoer hier . Ze implementeren de string.revse extension-methode. Hiermee kunt u code als volgt schrijven:

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

Ze gebruiken ook de Tochararray / Reverse Combination die andere antwoorden op deze vraag suggereren. De broncode ziet er als volgt uit:

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

Antwoord 10, autoriteit 2%

Ten eerste hoeft u ToCharArrayniet aan te roepen, aangezien een string al kan worden geïndexeerd als een char-array, dus dit bespaart u een toewijzing.

De volgende optimalisatie is het gebruik van een StringBuilderom onnodige toewijzingen te voorkomen (aangezien strings onveranderlijk zijn, maakt het aaneenschakelen ervan elke keer een kopie van de string). Om dit verder te optimaliseren, hebben we de lengte van de StringBuildervooraf ingesteld, zodat het zijn buffer niet hoeft uit te breiden.

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

Bewerken: prestatiegegevens

Ik heb deze functie en de functie getest met behulp van Array.Reversemet het volgende eenvoudige programma, waarbij Reverse1één functie is en Reverse2de andere:

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

Het blijkt dat voor korte snaren de Array.Reverse-methode ongeveer twee keer zo snel is als die hierboven, en voor langere snaren is het verschil nog meer uitgesproken. Dus aangezien de Array.Reversemethode zowel eenvoudiger als sneller is, raad ik aan dat u dat gebruikt in plaats van deze. Ik laat deze hierheen om te laten zien dat het niet de manier is om het te doen (veel tot mijn verbazing!)


Antwoord 11, Autoriteit 2%

Als u een echt gevaarlijk spel wilt spelen, dan is dit veruit de snelste manier, er is (ongeveer vier keer sneller dan de Array.Reversemethode). Het is een in-place omgekeerd met behulp van Pointers.

Merk op dat ik dit echt niet aanraden voor elk gebruik, ooit (Kijk hier om enkele redenen waarom u deze methode niet mag gebruiken, maar het is gewoon interessant om te zien dat het kan worden gedaan, en dat de snaren eenmaal niet echt onveranderlijk zijn U schakelt onveilige code aan.

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

Antwoord 12, Autoriteit 2%

Probeer array.Reverse

te gebruiken


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

Antwoord 13

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

Natuurlijk kun je de stringklasse uitbreiden met de omgekeerde methode

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

Antwoord 14

‘Beste’ kan van veel dingen afhangen, maar hier zijn nog een paar korte alternatieven, gerangschikt van snel naar langzaam:

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̽"  👍

Antwoord 15

Vanaf .NET Core 2.1 is er een nieuwe manier om een string om te keren met behulp van de string.Createmethode.

Merk op dat deze oplossing Unicode-combinaties van tekens enz. niet correct verwerkt, omdat “Les Mise\u0301rables” zou worden geconverteerd naar “selbarésiM seL”. De de andere antwoordenvoor een betere oplossing.

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

Dit kopieert in wezen de karakters van inputnaar een nieuwe string en keert de nieuwe string ter plekke om.

Waarom is string.Createnuttig?

Als we een string maken van een bestaande array, wordt een nieuwe interne array toegewezen en worden de waarden gekopieerd. Anders zou het mogelijk zijn om een string te muteren nadat deze is aangemaakt (in een veilige omgeving). Dat wil zeggen, in het volgende fragment moeten we een array met lengte 10 twee keer toewijzen, één als buffer en één als de interne array van de string.

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

string.Createstelt ons in wezen in staat om de interne array te manipuleren tijdens de aanmaaktijd van de string. Dit wil zeggen, we hebben geen buffer meer nodig en kunnen daarom voorkomen dat die ene char-array wordt toegewezen.

Steve Gordon heeft er meer in detail over geschreven hier. Er is ook een artikel op MSDN.

Hoe string.Createte gebruiken?

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

De methode duurt drie parameters:

  1. de lengte van de tekenreeks om te maken,
  2. De gegevens die u wilt gebruiken om de nieuwe reeks dynamisch te maken,
  3. en een afgevaardigde die de laatste reeks van de gegevens creëert, waarbij de eerste parameter wijst naar de interne chararray van de nieuwe reeks en de tweede is de gegevens (staat) die u hebt gepasseerd naar string.Create.

In de afgevaardigde kunnen we opgeven hoe de nieuwe reeks is gemaakt van de gegevens. In ons geval kopiëren we gewoon de tekens van de invoerreeks naar de Spangebruikt door de nieuwe reeks. Dan keren we de Spanen vandaar de hele reeks omgekeerd.

Benchmarks

Om mijn voorgestelde manier van omkeren van een string met het geaccepteerde antwoord, heb ik twee benchmarks geschreven met behulp van 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);
}

Hier zijn de resultaten op mijn computer:

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

Zoals je kunt zien, wijzen we met ReverseWithStringCreateslechts de helft van het geheugen toe dat wordt gebruikt door de ReverseWithArray-methode.


Antwoord 16

Maak je geen zorgen over een functie, doe het gewoon op zijn plaats. Opmerking: de tweede regel zal een argumentuitzondering genereren in het venster Direct van sommige VS-versies.

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

Antwoord 17

Sorry voor het lange bericht, maar dit kan interessant zijn

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

Resultaten:

  • Omgekeerde karakterbuffer gebruiken: 346 ms
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • OmgekeerdeXOR gebruiken: 319ms

Antwoord 18

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

Uitvoer

Voor maat: 10

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

Voor maat: 100

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

Voor maat: 1000

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

Voor maat: 10000

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

Antwoord 19

Eenvoudigste manier:

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

Antwoord 20

Op stapels gebaseerde oplossing.

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

Of

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

Antwoord 21

Wat dacht je van:

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

Antwoord 22

Ik heb een C#-poort gemaakt van Microsoft.VisualBasic.Strings. Ik weet niet zeker waarom ze zulke handige functies (van VB) buiten de System.String in Framework houden, maar nog steeds onder Microsoft.VisualBasic. Hetzelfde scenario voor financiële functies (bijv. 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();
}

Antwoord 23

Moest een recursief voorbeeld indienen:

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

Antwoord 24

Sorry voor het posten in dit oude topic. Ik oefen wat code voor een interview.

Dit was wat ik bedacht voor C#. Mijn eerste versie voor de refactoring was verschrikkelijk.

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 tegenstelling tot de Array.Reverse-methode hieronder, wordt het sneller weergegeven met 12 tekens of minder in de tekenreeks. Na 13 tekens begint de Array.Reversesneller te worden en domineert het uiteindelijk behoorlijk zwaar op snelheid. Ik wilde gewoon wijzen op ongeveer waar de snelheid begint te veranderen.

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

Bij 100 tekens in de tekenreeks, het is sneller dan mijn versie x 4. Als ik echter wist dat de snaren altijd minder dan 13 karakters zouden zijn, zou ik degene gebruiken die ik heb gemaakt.

Testen is uitgevoerd met Stopwatchen 5000000 iteraties. Ook ben ik niet zeker of mijn versie surrogaten of gecombineerde tekensituaties behandelt met UnicodeCodering.


Antwoord 25

“Better Way” is afhankelijk van wat belangrijker voor u is in uw situatie, prestaties, elegantie, onderhoudbaarheid enz.

Hoe dan ook, hier is een aanpak met 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);

Antwoord 26

Als het ooit in een interview is gekomen en je werd verteld dat je geen array kunt gebruiken. Revresse, ik denk dat dit een van de snelste kan zijn. Het creëert geen nieuwe snaren en itereert slechts meer dan de helft van de 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);
    }

Antwoord 27

Als je een string hebt die alleen ASCII-tekens bevat, kun je deze methode gebruiken.

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

Antwoord 28

Allereerst wat je moet begrijpen is dat str+= je stringgeheugen zal verkleinen om ruimte te maken voor 1 extra char. Dit is prima, maar als je bijvoorbeeld een boek met 1000 pagina’s hebt dat je wilt omdraaien, duurt het erg lang om dit uit te voeren.

De oplossing die sommige mensen zouden kunnen voorstellen, is het gebruik van StringBuilder. Wat stringbuilder doet als je een += uitvoert, is dat het veel grotere stukken geheugen toewijst om het nieuwe teken vast te houden, zodat het niet elke keer dat je een char toevoegt een nieuwe toewijzing hoeft uit te voeren.

Als je echt een snelle en minimale oplossing wilt, raad ik het volgende aan:

           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 deze oplossing is er één initiële geheugentoewijzing wanneer de char[] wordt geïnitialiseerd en één toewijzing wanneer de tekenreeksconstructor de tekenreeks opbouwt vanuit de char-array.

Op mijn systeem heb ik een test voor je uitgevoerd die een reeks van 2 750 000 tekens omkeert. Hier zijn de resultaten voor 10 uitvoeringen:

StringBuilder: 190K – 200K ticks

Char-array: 130K – 160K ticks

Ik heb ook een test uitgevoerd voor normale String +=, maar ik heb deze na 10 minuten zonder resultaat afgebroken.

Ik merkte echter ook dat voor kleinere strings de StringBuilder sneller is, dus je zult op basis van de invoer moeten beslissen over de implementatie.

Proost


Antwoord 29

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

Antwoord 30

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

Other episodes