web-dev-qa-db-ja.com

文字列を反転するための最良の方法

私はちょうどC#2.0で文字列逆関数を書かなければならなかった(すなわちLINQは利用できない)そしてこれを思いついた:

public string Reverse(string text)
{
    char[] cArray = text.ToCharArray();
    string reverse = String.Empty;
    for (int i = cArray.Length - 1; i > -1; i--)
    {
        reverse += cArray[i];
    }
    return reverse;
}

個人的に私はその機能に夢中ではなく、それをするより良い方法があると確信しています。ある?

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

ここでは、文字列"Les Mise\u0301rables""selbare\u0301siM seL"として正しく反転する解決策を示します。これは、コード単位(selbarésiM seLなど)、またはコードポイント(サロゲートペアに特別な注意を払って反転させる)に基づく実装の結果がそうであるように、selbaŕesiM seL(アクセントの位置に注意)ではなくArray.Reverseのようにレンダリングする必要があります。

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

public static class Test
{
    private static IEnumerable<string> GraphemeClusters(this string s) {
        var enumerator = StringInfo.GetTextElementEnumerator(s);
        while(enumerator.MoveNext()) {
            yield return (string)enumerator.Current;
        }
    }
    private static string ReverseGraphemeClusters(this string s) {
        return string.Join("", s.GraphemeClusters().Reverse().ToArray());
    }

    public static void Main()
    {
        var s = "Les Mise\u0301rables";
        var r = s.ReverseGraphemeClusters();
        Console.WriteLine(r);
    }
}

(そしてここでライブランニングの例: https://ideone.com/DqAeMJ

これは、書記素クラスタの反復処理に.NET APIを使用しているだけです は以前からありましたが、ビューからは少し「隠されている」ようです。

158

これは驚くほどトリッキーな質問であることがわかりました。

Array.Reverseはネイティブにコード化されており、保守と理解が非常に簡単なので、ほとんどの場合Array.Reverseを使用することをお勧めします。

私がテストしたすべてのケースでStringBuilderよりも優れているようです。

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

Xまたは を使用する特定の文字列長に対して、より高速になることができる2番目のアプローチがあります。

    public static string ReverseXor(string s)
    {
        if (s == null) return null;
        char[] charArray = s.ToCharArray();
        int len = s.Length - 1;

        for (int i = 0; i < len; i++, len--)
        {
            charArray[i] ^= charArray[len];
            charArray[len] ^= charArray[i];
            charArray[i] ^= charArray[len];
        }

        return new string(charArray);
    }

完全なUnicode UTF16文字セットをサポートしたい場合は これを読んでください 。そして代わりにその実装を使用してください。上記のいずれかのアルゴリズムを使用し、文字が逆になった後にそれをクリーンアップするために文字列を実行することで、さらに最適化できます。

これは、StringBuilder、Array.Reverse、およびXorメソッドのパフォーマンス比較です。

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

結果は次のとおりです。

26251 Ticks String Builder (Length: 1) : called 10000 times.
33373 Ticks Array.Reverse (Length: 1) : called 10000 times.
20162 Ticks Xor (Length: 1) : called 10000 times.

51321 Ticks String Builder (Length: 10) : called 10000 times.
37105 Ticks Array.Reverse (Length: 10) : called 10000 times.
23974 Ticks Xor (Length: 10) : called 10000 times.

66570 Ticks String Builder (Length: 15) : called 10000 times.
26027 Ticks Array.Reverse (Length: 15) : called 10000 times.
24017 Ticks Xor (Length: 15) : called 10000 times.

101609 Ticks String Builder (Length: 25) : called 10000 times.
28472 Ticks Array.Reverse (Length: 25) : called 10000 times.
35355 Ticks Xor (Length: 25) : called 10000 times.

161601 Ticks String Builder (Length: 50) : called 10000 times.
35839 Ticks Array.Reverse (Length: 50) : called 10000 times.
51185 Ticks Xor (Length: 50) : called 10000 times.

230898 Ticks String Builder (Length: 75) : called 10000 times.
40628 Ticks Array.Reverse (Length: 75) : called 10000 times.
78906 Ticks Xor (Length: 75) : called 10000 times.

312017 Ticks String Builder (Length: 100) : called 10000 times.
52225 Ticks Array.Reverse (Length: 100) : called 10000 times.
110195 Ticks Xor (Length: 100) : called 10000 times.

2970691 Ticks String Builder (Length: 1000) : called 10000 times.
292094 Ticks Array.Reverse (Length: 1000) : called 10000 times.
846585 Ticks Xor (Length: 1000) : called 10000 times.

305564115 Ticks String Builder (Length: 100000) : called 10000 times.
74884495 Ticks Array.Reverse (Length: 100000) : called 10000 times.
125409674 Ticks Xor (Length: 100000) : called 10000 times.

Xorは短い文字列の方が速いようです。

122
Sam Saffron

文字列にUnicodeデータ(厳密には非BMP文字)が含まれていると、文字列を反転するときに上位と下位のサロゲートコード単位の順序を入れ替えることができないため、投稿された他のメソッドによって破損します。 (これに関するより多くの情報は 私のブログ で見つけることができます。)

次のコードサンプルは、BMP以外の文字を含む文字列、たとえば "\ 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);
}
49

上から3.5フレームワーク

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

わかりました、「繰り返してはいけません」という目的で、次の解決策を提案します。

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

私の理解するところでは、VB.NETでデフォルトで利用可能なこの実装はUnicode文字を適切に処理します。

23
richardtallent

Greg Beechが、確かに可能な限り速いunsafeオプションをポストしました(インプレース反転です)。しかし、彼が答えで示したように、それは完全に悲惨な考えです。

そうは言っても、Array.Reverseが最速の方法であるという点で非常に多くのコンセンサスがあることに驚きました。文字列の反転コピーを返す(インプレース反転の俗語はありません)小さな文字列の場合のArray.Reverseメソッドよりもはるかに速い)unsafeのアプローチがまだあります。

public static unsafe string Reverse(string text)
{
    int len = text.Length;

    // Why allocate a char[] array on the heap when you won't use it
    // outside of this method? Use the stack.
    char* reversed = stackalloc char[len];

    // Avoid bounds-checking performance penalties.
    fixed (char* str = text)
    {
        int i = 0;
        int j = i + len - 1;
        while (i < len)
        {
            reversed[i++] = str[j--];
        }
    }

    // Need to use this overload for the System.String constructor
    // as providing just the char* pointer could result in garbage
    // at the end of the string (no guarantee of null terminator).
    return new string(reversed, 0, len);
}

これはいくつかのベンチマーク結果です

文字列が大きくなるにつれて、パフォーマンスの向上は縮小し、Array.Reverseメソッドに対して消えていくことがわかります。しかし、中小サイズの文字列の場合は、この方法に勝ることは困難です。

17
Dan Tao

簡単でいい答えは、拡張メソッドを使うことです。

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

これが出力です。

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

本当に危険なゲームをプレイしたいのであれば、これがはるかに速い方法です(Array.Reverseメソッドよりも約4倍高速です)。これはポインタを使ったインプレースリバースです。

私は本当にこれをどんな用途にも推奨しないことに注意してください( この方法を使うべきでない理由がいくつかあります を参照してください)。安全でないコードをオンにすると、本当に不変になることはありません。

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

ウィキペディアのエントリー here を見てください。それらはString.Reverse拡張メソッドを実装します。これにより、次のようなコードを書くことができます。

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

また、この質問に対する他の回答からも示唆されるToCharArray/Reverseの組み合わせも使用されています。ソースコードは次のようになります。

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

まず、文字列がすでにchar配列としてインデックス付けされている可能性があるので、ToCharArrayを呼び出す必要はありません。そのため、割り当てを節約できます。

次の最適化は、不要な割り当てを防ぐためにStringBuilderを使用することです(文字列は不変であるため、それらを連結すると毎回文字列のコピーが作成されます)。これをさらに最適化するためにStringBuilderの長さをプリセットしてバッファを拡張する必要がないようにします。

public string Reverse(string text)
{
    if (string.IsNullOrEmpty(text))
    {
        return text;
    }

    StringBuilder builder = new StringBuilder(text.Length);
    for (int i = text.Length - 1; i >= 0; i--)
    {
        builder.Append(text[i]);
    }

    return builder.ToString();
}

編集:パフォーマンスデータ

この関数とArray.Reverseを使った関数を次の簡単なプログラムでテストしました。ここでReverse1は一方の関数で、Reverse2はもう一方の関数です。

static void Main(string[] args)
{
    var text = "abcdefghijklmnopqrstuvwxyz";

    // pre-jit
    text = Reverse1(text); 
    text = Reverse2(text);

    // test
    var timer1 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse1(text);
    }

    timer1.Stop();
    Console.WriteLine("First: {0}", timer1.ElapsedMilliseconds);

    var timer2 = Stopwatch.StartNew();
    for (var i = 0; i < 10000000; i++)
    {
        text = Reverse2(text);
    }

    timer2.Stop();
    Console.WriteLine("Second: {0}", timer2.ElapsedMilliseconds);

    Console.ReadLine();
}

短い文字列ではArray.Reverseメソッドは上記のものの約2倍の速さで、長い文字列では違いはさらに顕著になります。 Array.Reverseメソッドがよりシンプルで速いということを考えれば、私はあなたがこれよりもあなたがそれを使うことを勧めます。私はこれをあなたがそれをするべき方法ではないことを示すためだけにここに残しておきます(驚いたことに!)

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

もちろん、Reverseメソッドで文字列クラスを拡張することができます

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

Array.Reverseを使ってみてください


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

関数を気にするな注:2行目では、一部のVSバージョンの[イミディエイト]ウィンドウで引数の例外が発生します。

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

「最高」は多くのことに左右される可能性がありますが、ここでは速いものから遅いものへと並べられた、いくつかの短い代替案を挙げます。

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

再帰的な例を提出しなければならなかった:

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

出力

サイズの場合:10

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

サイズ:100

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

サイズ:1000

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

用サイズ:10000

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

長い投稿で申し訳ありませんが、これは面白いかもしれません

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

結果:

  • ReverseUsingCharacterBuffer:346ms
  • ReverseUsingArrayClass:87ms
  • ReverseUsingStringBuilder:824ミリ秒
  • ReverseUsingStack:2086ms
  • ReverseUsingXOR:319ms
4
aku

スタックベースのソリューション.

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

または

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

Microsoft.VisualBasic.Strings からC#ポートを作成しました。 FrameworkのSystem.Stringの外側で、まだMicrosoft.VisualBasicの下でそのような便利な関数が(VBから)保持されている理由はわかりません。財務機能に関する同じシナリオ(例: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

.NET Core 2.1以降では、string.Createメソッドを使用して文字列を反転する新しい方法があります。

「Les Mise\u0301rables」は「selbarésiMseL」に変換されるため、このソリューションはUnicode文字の結合などを正しく処理しないことに注意してください。 他の答え より良い解決策のために。

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

これは、基本的にinputの文字を新しい文字列にコピーし、新しい文字列をインプレースで反転します。

string.Createが役立つのはなぜですか?

既存の配列から文字列を作成すると、新しい内部配列が割り当てられ、値がコピーされます。そうしないと、作成後に文字列を変更することが可能になります(安全な環境で)。つまり、次のスニペットでは、長さ10の配列を2回割り当てる必要があります。1つはバッファーとして、もう1つは文字列の内部配列としてです。

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

string.Createを使用すると、文字列の作成時に内部配列を操作できます。これは、もうバッファを必要としないため、その1つのchar配列の割り当てを回避できます。

Steve Gordonがそれについてもっと詳しく書いています hereMSDN に関する記事もあります。

string.Createの使用方法

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

このメソッドは3つのパラメーターを取ります。

  1. 作成する文字列の長さ、
  2. 新しい文字列を動的に作成するために使用するデータ、
  3. データから最終的な文字列を作成するデリゲート。最初のパラメーターは新しい文字列の内部char配列を指し、2番目はstring.Createに渡したデータ(状態)です。

デリゲート内では、データから新しい文字列を作成する方法を指定できます。この場合、入力文字列の文字を、新しい文字列で使用されるSpanにコピーするだけです。次に、Spanを逆にします。したがって、文字列全体が逆になります。

ベンチマーク

提案された文字列の反転方法と受け入れられた答えを比較するために、BenchmarkDotNetを使用して2つのベンチマークを作成しました。

public class StringExtensions
{
    public static string ReverseWithArray(string input)
    {
        var charArray = input.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }

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

[MemoryDiagnoser]
public class StringReverseBenchmarks
{
    private string input;

    [Params(10, 100, 1000)]
    public int InputLength { get; set; }


    [GlobalSetup]
    public void SetInput()
    {
        // Creates a random string of the given length
        this.input = RandomStringGenerator.GetString(InputLength);
    }

    [Benchmark(Baseline = true)]
    public string WithReverseArray() => StringExtensions.ReverseWithArray(input);

    [Benchmark]
    public string WithStringCreate() => StringExtensions.ReverseWithStringCreate(input);
}

私のマシンでの結果は次のとおりです。

| Method           | InputLength |         Mean |      Error |    StdDev |  Gen 0 | Allocated |
| ---------------- | ----------- | -----------: | ---------: | --------: | -----: | --------: |
| WithReverseArray | 10          |    45.464 ns |  0.4836 ns | 0.4524 ns | 0.0610 |      96 B |
| WithStringCreate | 10          |    39.749 ns |  0.3206 ns | 0.2842 ns | 0.0305 |      48 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 100         |   175.162 ns |  2.8766 ns | 2.2458 ns | 0.2897 |     456 B |
| WithStringCreate | 100         |   125.284 ns |  2.4657 ns | 2.0590 ns | 0.1473 |     232 B |
|                  |             |              |            |           |        |           |
| WithReverseArray | 1000        | 1,523.544 ns |  9.8808 ns | 8.7591 ns | 2.5768 |    4056 B |
| WithStringCreate | 1000        | 1,078.957 ns | 10.2948 ns | 9.6298 ns | 1.2894 |    2032 B |

ご覧のとおり、ReverseWithStringCreateを使用すると、ReverseWithArrayメソッドで使用されるメモリの半分しか割り当てられません。

3
Flogex

どうですか?

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

この古いスレッドに投稿してすみません。私はインタビューのためにいくつかのコードを練習しています。

これが私がC#に思い付いたことです。リファクタリングの前の私の最初のバージョンはひどいものでした。

static String Reverse2(string str)
{
    int strLen = str.Length, elem = strLen - 1;
    char[] charA = new char[strLen];

    for (int i = 0; i < strLen; i++)
    {
        charA[elem] = str[i];
        elem--;
    }

    return new String(charA);
}

以下のArray.Reverseメソッドとは対照的に、文字列の12文字以下のほうが早く表示されます。 13文字を過ぎると、Array.Reverseが速くなり始め、最終的には速度がかなり重視されます。私はちょうど速度が変化し始めるところをおおよそ指摘したかったです。

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

    Array.Reverse(charA);

    return new String(charA);
}

文字列の100文字で、それは私のバージョンx 4よりも速いです。

テストはStopwatchと5000000の繰り返しで行われました。また、私のバージョンがサロゲートを扱うのかUnicodeエンコーディングを使った文字の組み合わせの状況を扱うのかもわかりません。

3
Jason Ausborn

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

これと同じくらい簡単:

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

output を参照してください。

2
Raktim Biswas

ASCII文字のみを含む文字列がある場合は、この方法を使用できます。

    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

もしインタビューに出て、Array.Reverseを使うことはできないと言われたら、これが最速の方法かもしれないと思います。これは新しい文字列を作成せず、配列の半分以上を反復します(つまりO(n/2)反復)。

    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

「より良い方法」は、あなたの状況、パフォーマンス、優雅さ、保守性などにおいてあなたにとってより重要なものによって異なります。

とにかく、これは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

string.Createを使用してパフォーマンスと割り当てを低くし、正確性を確保するためにStringInfoクラスを使用する2つの答えが好きなので、組み合わせたアプローチが必要であると判断しました。これは究極の文字列反転方法です:)

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

StringInfoクラスのメソッドを使用するより良い方法があります。これは、インデックスだけを返すことにより、Enumeratorによる多くの文字列割り当てをスキップします。

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

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

これは安全にUnicodeを処理する拡張として書かれた、この関数のUnicodeセーフバージョンです。マークされた完全な回答に近いですが、「無効な上位サロゲート文字」の例外をスローしません。

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

上記の方法より速い

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

LINQの集計関数を使用する

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

まず第一にあなたが理解しなければならないことは、str + =があなたの文字列メモリを1文字余分のスペースを作るためにリサイズするということです。これは問題ありませんが、たとえば、反転したい1000ページの本がある場合は、実行に非常に時間がかかります。

何人かの人々が提案するかもしれない解決策はStringBuilderを使うことです。 + =を実行したときに文字列ビルダーが行うことは、新しい文字を保持するためにはるかに大きいチャンクのメモリを割り当てるため、charを追加するたびに再割り当てを行う必要がないことです。

あなたが本当に速くて最小限の解決策を望むならば、私は以下を提案するでしょう:

            char[] chars = new char[str.Length];
            for (int i = str.Length - 1, j = 0; i >= 0; --i, ++j)
            {
                chars[j] = str[i];
            }
            str = new String(chars);

このソリューションでは、char []が初期化されたときに1つの初期メモリ割り当てがあり、文字列コンストラクタがchar配列から文字列を構築するときに1つの割り当てがあります。

私のシステムでは、私はあなたのために2 75万文字の文字列を逆にするテストを実行しました。これが10回の実行の結果です。

StringBuilder:190K - 200K tick

Char Array:130K - 160K tick

通常のString + =のテストも実行しましたが、10分後に出力を中止して放棄しました。

ただし、小さい文字列の場合はStringBuilderの方が速いので、入力に基づいて実装を決定する必要があります。

乾杯

1
Reasurria

最も簡単な方法:

string reversed = new string(text.Reverse().ToArray());
1
Shady Sirhan
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
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

文字列を反転させるにはさまざまな方法があります。そのうちの3つを以下に示します。

- Array.Reverse関数を使用する。

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

- 文字列のみを使用

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

- char配列のみを使用

 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

私は面接で同様の質問をしました。これは私の回答でしたが、おそらく他の回答ほどパフォーマンスは速くありません。私の質問は「逆方向に文字列を出力するメソッドを持つことができるクラスを作る」と言いました:

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