Cara terbaik untuk membalik string


441

Saya baru saja menulis fungsi string reverse di C # 2.0 (yaitu LINQ tidak tersedia) dan muncul dengan ini:

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

Secara pribadi saya tidak tergila-gila dengan fungsi dan yakin bahwa ada cara yang lebih baik untuk melakukannya. Disana?


51
Sangat mengejutkan jika Anda menginginkan dukungan internasional yang tepat. Contoh: Kroasia / Serbia memiliki dua karakter huruf lj, nj dll. Kebalikan yang tepat dari "ljudi" adalah "idulj", BUKAN "idujl". Saya yakin Anda akan ongkos jauh lebih buruk ketika datang ke Arab, Thailand dll.
dbkk

Saya bertanya-tanya apakah itu lebih lambat untuk membuat string daripada menginisialisasi array temp dan menyimpan hasilnya di dalamnya, dan akhirnya mengubahnya menjadi string?
The Muffin Man

2
Utas terkait yang jauh lebih baru: Membalikkan string dengan aksen chars?
Jeppe Stig Nielsen

5
Pertanyaan ini dapat diperbaiki dengan mendefinisikan apa yang Anda maksud dengan "terbaik". Tercepat? Paling mudah dibaca? Paling andal di berbagai kasus tepi (cek nol, banyak bahasa, dll.)? Paling dapat dikelola di seluruh versi C # dan .NET?
hypehuman

Jawaban:


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

16
sambo99: Tidak perlu menyebutkan unicode: karakter di C # adalah karakter unicode, bukan byte. Xor mungkin lebih cepat, tetapi selain jauh lebih mudah dibaca, itu mungkin bahkan apa yang digunakan Array. Revers () menggunakan internal.
Nick Johnson

27
@Arachnid: Sebenarnya, karakter di C # adalah unit kode UTF-16; dibutuhkan dua dari mereka untuk mewakili karakter tambahan. Lihat jaggersoft.com/csharp_standard/9.4.1.htm .
Bradley Grainger

4
Ya sambo99 Saya kira Anda benar, tetapi ini adalah kasus yang sangat jarang untuk menggunakan UTF-32. Dan XOR hanya lebih cepat untuk rentang nilai yang sangat kecil, jawaban yang benar adalah menerapkan metode yang berbeda untuk panjang yang berbeda kurasa. Tapi ini jelas dan ringkas yang bermanfaat menurut saya.
PeteT

21
Karakter kontrol Unicode membuat metode ini tidak berguna untuk set karakter non latin. Lihat penjelasan Jon Skeet, menggunakan boneka kaus kaki: codeblog.jonskeet.uk/2009/11/02/… (1/4 jalan turun), atau video: vimeo.com/7516539
Callum Rogers

20
Semoga Anda tidak menemukan pengganti atau penggabungan karakter.
dalle

183

Di sini solusi yang membalikkan string "Les Mise\u0301rables"dengan benar "selbare\u0301siM seL". Ini harus memberikan seperti selbarésiM seL, bukan selbaŕesiM seL(perhatikan posisi aksen), seperti hasil sebagian besar implementasi berdasarkan unit kode ( Array.Reverse, dll) atau bahkan titik kode (membalikkan dengan perhatian khusus untuk pasangan pengganti).

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

(Dan contoh menjalankan langsung di sini: https://ideone.com/DqAeMJ )

Itu hanya menggunakan. NET API untuk iterasi cluster grapheme , yang telah ada sejak itu, tetapi sedikit "tersembunyi" dari pandangan, tampaknya.


10
+1 Salah satu dari sedikit jawaban yang benar, dan jauh lebih elegan dan bukti masa depan daripada yang lain, IMO
sehe

Ini gagal untuk beberapa hal yang bergantung pada lokal.
R. Martinho Fernandes

7
Sangat lucu bagaimana sebagian besar penjawab lain mencoba untuk memotong ms dari apa yang dinyatakan pendekatan yang salah. Sangat representatif.
G. Stoynev

2
Ini sebenarnya jauh lebih cepat untuk instantiate StringInfo (s), kemudian iterasi melalui SubstringByTextElements (x, 1) dan buat string baru dengan StringBuilder.

2
Agak aneh bahwa Anda telah menggunakan contoh Jon Skeet yang ia berikan tahun sebelumnya codeblog.jonskeet.uk/2009/11/02/... Les Misérables (meskipun Jon tidak menyebutkan solusi, ia hanya mendaftar masalah). Bagus bahwa Anda menemukan solusi. Mungkin Jon skeet menciptakan mesin waktu, kembali ke 2009 dan memposting contoh masalah yang Anda gunakan dalam solusi Anda.
barlop

126

Ini ternyata menjadi pertanyaan yang sangat rumit.

Saya akan merekomendasikan menggunakan Array. Membalik sebagian besar kasus karena dikodekan secara asli dan sangat mudah untuk mempertahankan dan memahami.

Tampaknya mengungguli StringBuilder dalam semua kasus yang saya uji.

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

Ada pendekatan kedua yang bisa lebih cepat untuk panjang string tertentu yang menggunakan 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);
    }

Catatan Jika Anda ingin mendukung rangkaian karakter Unicode UTF16 baca ini . Dan gunakan implementasi di sana sebagai gantinya. Ini dapat dioptimalkan lebih lanjut dengan menggunakan salah satu dari algoritma di atas dan berjalan melalui string untuk membersihkannya setelah karakter dibalik.

Berikut ini adalah perbandingan kinerja antara metode StringBuilder, Array.Reverse dan 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();
        }
    }
}

Inilah hasilnya:

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.

Tampaknya Xor bisa lebih cepat untuk string pendek.


2
Itu tidak mengembalikan string - Anda harus membungkus ini dalam panggilan ke "String baru (...)"
Greg Beech

BTW .. Saya baru saja melihat implementasi dari Array. Reverse, dan itu dilakukan secara naif untuk karakter ... itu harus jauh lebih cepat daripada opsi StringBuilder.
Sam Saffron

Betapa baiknya Anda, Greg, untuk menghentikan Sambo sampai pada solusi yang lebih baik daripada memilihnya dengan suara rendah.
DOK

@ dok1 - jangan menyebutkannya :) @ sambo99 - sekarang saya tertarik, harus mencabut kode profiler besok dan lihat!
Greg Beech

9
Metode ini tidak menangani string yang berisi karakter di luar Base Multilingual Plane, yaitu, karakter Unicode> = U + 10000 yang diwakili dengan dua karakter C #. Saya telah memposting jawaban yang menangani string semacam itu dengan benar.
Bradley Grainger

52

Jika Anda dapat menggunakan LINQ (.NET Framework 3.5+) daripada mengikuti satu liner akan memberi Anda kode pendek. Jangan lupa menambahkan using System.Linq;untuk memiliki akses ke Enumerable.Reverse:

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

Catatan:

  • bukan versi tercepat - menurut Martin Niederl 5.7 kali lebih lambat daripada pilihan tercepat di sini.
  • kode ini karena banyak opsi lain sama sekali mengabaikan segala macam kombinasi multi-karakter, jadi batasi penggunaan untuk tugas dan string pekerjaan rumah yang tidak mengandung karakter tersebut. Lihat jawaban lain dalam pertanyaan ini untuk implementasi yang benar menangani kombinasi tersebut.

Itu sekitar 5,7 kali lebih lambat daripada versi yang paling upvoted jadi saya tidak akan merekomendasikan menggunakan ini!
Martin Niederl

2
Bukan solusi tercepat, tetapi bermanfaat sebagai one-liner.
adrianmp

49

Jika string berisi data Unicode (karakter non-BMP), metode lain yang telah diposting akan merusaknya, karena Anda tidak dapat menukar urutan unit kode pengganti tinggi dan rendah saat membalikkan string. (Informasi lebih lanjut tentang ini dapat ditemukan di blog saya .)

Contoh kode berikut akan membalikkan string yang berisi karakter non-BMP dengan benar, mis., "\ U00010380 \ U00010381" (Huruf Ugaritik Alpa, Huruf Ugaritik 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);
}

29
Sebenarnya, karakter dalam C # adalah unit kode UTF-16 16-bit; karakter tambahan dikodekan menggunakan dua dari mereka, jadi ini perlu,
Bradley Grainger

14
Sepertinya System.String benar-benar harus mengekspos properti HereBeDragons untuk string yang berisi karakter tambahan Unicode.
Robert Rossney

4
@SebastianNegraszus: Itu benar: metode ini hanya membalikkan codepoint dalam string. Membalik cluster grapheme mungkin akan lebih "berguna" secara keseluruhan (tapi apa "penggunaan" dari membalikkan string arbitrer di tempat pertama?), Tetapi tidak mudah diimplementasikan dengan hanya metode built-in di .NET Framework.
Bradley Grainger

2
@ Richard: Aturan untuk memecah cluster grapheme sedikit lebih rumit daripada hanya mendeteksi penggabungan poin kode; lihat dokumentasi tentang Grapheme Cluster Boundaries di UAX # 29 untuk informasi lebih lanjut.
Bradley Grainger

1
Info yang sangat bagus! Apakah SIAPA PUN memiliki tes gagal untuk tes Array.Reverse? Dan dengan tes yang saya maksud string sampel bukan tes unit keseluruhan ... Ini akan sangat membantu saya (dan orang lain) meyakinkan orang yang berbeda tentang masalah ini ..
Andrei Rînea

25

Oke, demi "jangan ulangi diri Anda," saya menawarkan solusi berikut:

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

Pemahaman saya adalah bahwa implementasi ini, tersedia secara default di VB.NET, dengan benar menangani karakter Unicode.


11
Ini hanya menangani pengganti dengan benar. Ini mengacaukan menggabungkan tanda: ideone.com/yikdqX .
R. Martinho Fernandes

17

Greg Beech memposting unsafeopsi yang memang secepat mungkin (ini merupakan pembalikan di tempat); tetapi, seperti yang ditunjukkannya dalam jawabannya, itu adalah gagasan yang benar-benar membawa malapetaka .

Yang mengatakan, saya terkejut ada begitu banyak konsensus yang Array.Reversemerupakan metode tercepat. Masih ada unsafependekatan yang mengembalikan salinan string yang terbalik (tidak ada shenanigans pembalikan di tempat) secara signifikan lebih cepat daripada Array.Reversemetode untuk string kecil:

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

Berikut ini beberapa hasil benchmark .

Anda dapat melihat bahwa peningkatan kinerja menyusut dan kemudian menghilang terhadap Array.Reversemetode saat string semakin besar. Untuk string berukuran kecil hingga sedang, sulit untuk mengalahkan metode ini.


2
StackOverflow pada string besar.
Raz Megrelidze

@rezomegreldize: Ya, itu akan terjadi;)
Dan Tao

15

Jawaban yang mudah dan menyenangkan menggunakan Metode Ekstensi:

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

dan inilah hasilnya:

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

Reverse()dan ToArray()berada dalam urutan yang salah dalam sampel kode Anda.
Chris Walsh

Apa tujuan layanan @?
user5389726598465

2
@ user5389726598465 Lihat tautan ini: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… Karena 'base' adalah kata kunci dalam C #, itu harus diawali dengan @ untuk kompiler C # untuk menafsirkannya sebagai pengidentifikasi.
Dyndrilliac

14

Jika Anda ingin memainkan permainan yang benar-benar berbahaya, maka ini adalah cara tercepat yang ada (sekitar empat kali lebih cepat daripada Array.Reversemetode ini). Ini adalah terbalik menggunakan pointer.

Perhatikan bahwa saya benar-benar tidak merekomendasikan ini untuk penggunaan apa pun, pernah ( lihat di sini untuk beberapa alasan mengapa Anda tidak harus menggunakan metode ini ), tetapi hanya menarik untuk melihat bahwa itu dapat dilakukan, dan string tidak benar-benar berubah. setelah Anda mengaktifkan kode yang tidak aman.

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

Saya cukup yakin ini akan mengembalikan hasil yang salah untuk string utf16, itu benar-benar meminta masalah :)
Sam Saffron

Hai Anda harus menautkan ke posting ini di stackoverflow.com/questions/229346/… ini , seperti yang saya katakan sebelumnya ini benar-benar meminta masalah ...
Sam Saffron

Ini mungkin benar-benar jahat dan keliru (seperti Anda sendiri mengakui), tetapi masih ada cara kinerja tinggi untuk membalikkan string menggunakan unsafekode yang tidak jahat dan masih berdetak Array.Reversedalam banyak kasus. Lihatlah jawaban saya.
Dan Tao

13

Lihatlah entri wikipedia di sini . Mereka menerapkan metode ekstensi String.Reverse. Ini memungkinkan Anda untuk menulis kode seperti ini:

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

Mereka juga menggunakan kombinasi ToCharArray / Reverse yang disarankan oleh jawaban lain untuk pertanyaan ini. Kode sumber terlihat seperti ini:

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

Itu luar biasa, kecuali metode ekstensi tidak diperkenalkan di c # 2.0.
Kobi

11

Pertama, Anda tidak perlu memanggil ToCharArraystring yang sudah dapat diindeks sebagai array char, jadi ini akan menghemat alokasi.

Optimalisasi selanjutnya adalah menggunakan a StringBuilderuntuk mencegah alokasi yang tidak perlu (karena string tidak dapat diubah, menyatukannya membuat salinan string setiap kali). Untuk lebih mengoptimalkan ini, kami telah mengatur sebelumnya panjang StringBuildersehingga tidak perlu memperluas buffer-nya.

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: Data Kinerja

Saya menguji fungsi ini dan menggunakan fungsi Array.Reversedengan program sederhana berikut, di mana Reverse1satu fungsi dan Reverse2yang lainnya:

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

Ternyata untuk string pendek Array.Reversemetode ini sekitar dua kali lebih cepat dari yang di atas, dan untuk string yang lebih panjang perbedaannya bahkan lebih jelas. Jadi mengingat bahwa Array.Reversemetode ini lebih sederhana dan lebih cepat, saya sarankan Anda menggunakannya daripada yang ini. Saya meninggalkan yang ini di sini hanya untuk menunjukkan bahwa itu bukan cara yang harus Anda lakukan (sangat mengejutkan saya!)


Tidak akan menyimpan teks. Panjang variabel memberikan kecepatan lebih saat Anda mereferensikan ini melalui objek?
David Robbins

10

Coba gunakan Array. Balikkan


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

Ini sangat cepat.
Michael Stum

Mengapa memilih bawah? Bukan berdebat, tapi saya lebih suka belajar dari kesalahan saya.
Mike Two

Gagal menangani penggabungan poin kode di antara banyak hal lainnya.
Mooing Duck

@ MoingDuck - terima kasih telah menjelaskan, tapi saya tidak tahu apa yang Anda maksud dengan poin kode. Anda juga bisa menguraikan "banyak hal lain".
Mike Two

@ MoooDuck Saya mencari poin kode. Iya. Anda benar. Itu tidak menangani poin kode. Sulit untuk menentukan semua persyaratan untuk pertanyaan yang tampak sederhana. Terima kasih atas umpan baliknya
Mike Two

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

Tentu saja Anda dapat memperluas kelas string dengan metode Reverse

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

Enumerable.Reverse(input)sama denganinput.Reverse()
fubo

8

"Terbaik" dapat bergantung pada banyak hal, tetapi berikut beberapa alternatif pendek yang dipesan dari cepat ke lambat:

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

8

Dimulai dengan .NET Core 2.1 ada cara baru untuk membalikkan string menggunakan string.Create metode ini.

Perhatikan bahwa solusi ini tidak menangani Unicode yang menggabungkan karakter dll dengan benar, karena "Les Mise \ u0301rables" akan dikonversi menjadi "selbarésiM seL". The jawaban yang lain untuk solusi yang lebih baik.

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

Ini pada dasarnya menyalin karakter inputke string baru dan membalikkan string baru di tempat.

Kenapa string.Createbermanfaat?

Ketika kami membuat string dari array yang ada, array internal baru dialokasikan dan nilai-nilai disalin. Kalau tidak, akan mungkin untuk memutasikan string setelah pembuatannya (dalam lingkungan yang aman). Yaitu, dalam cuplikan berikut ini, kami harus mengalokasikan array dengan panjang 10 dua kali, satu sebagai buffer dan satu sebagai array internal string.

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

string.Createdasarnya memungkinkan kita untuk memanipulasi array internal selama waktu pembuatan string. Ini, kita tidak perlu buffer lagi dan karena itu dapat menghindari mengalokasikan satu array char.

Steve Gordon telah menulisnya secara lebih rinci di sini . Ada juga artikel tentang MSDN .

Cara menggunakan string.Create?

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

Metode ini mengambil tiga parameter:

  1. Panjang string yang akan dibuat,
  2. data yang ingin Anda gunakan untuk secara dinamis membuat string baru,
  3. dan delegasi yang membuat string terakhir dari data, di mana parameter pertama menunjuk ke chararray internal dari string baru dan yang kedua adalah data (status) yang Anda berikan string.Create.

Di dalam delegasi kita dapat menentukan bagaimana string baru dibuat dari data. Dalam kasus kami, kami hanya menyalin karakter dari string input ke yang Spandigunakan oleh string baru. Kemudian kita membalikkannyaSpan dan karenanya seluruh string dibalik.

Tolak ukur

Untuk membandingkan cara saya yang diusulkan untuk membalik string dengan jawaban yang diterima, saya telah menulis dua tolok ukur menggunakan 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);
}

Inilah hasil di mesin saya:

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

Seperti yang Anda lihat, dengan ReverseWithStringCreatekami mengalokasikan hanya setengah dari memori yang digunakan oleh ReverseWithArraymetode ini.


Ini jauh lebih cepat daripada Linq membalikkan
code4j

7

Jangan repot-repot dengan fungsi, lakukan saja di tempat. Catatan: Baris kedua akan memunculkan eksepsi argumen di jendela Immediate dari beberapa versi VS.

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

1
Beberapa pria meluangkan waktu untuk menurunkan setiap jawaban (termasuk saya) tanpa menjelaskan alasannya.
Marcel Valdez Orozco

Ini tidak benar-benar di tempat, karena Anda membuatnew string
mbadawi23

5

Maaf untuk posting lama, tapi ini mungkin menarik

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

Hasil:

  • MembalikkanMenggunakanCharacterBuffer: 346ms
  • ReverseUsingArrayClass: 87ms
  • ReverseUsingStringBuilder: 824ms
  • ReverseUsingStack: 2086ms
  • ReverseUsingXOR: 319ms

Saya menambahkan perbandingan serupa di posting saya, ini adalah wiki komunitas sehingga Anda harus dapat mengedit. Kinerja sangat tergantung pada panjang string serta algoritma, akan menarik untuk menggambarkannya. Saya masih berpikir Array. Balik akan menjadi yang tercepat dalam semua kasus ...
Sam Saffron

"akan menjadi tercepat dalam semua kasus" ketika fungsi TrySZReverse ajaib (digunakan dalam implementasi Reverse) gagal, Array. Membalikkan mundur ke implementasi sederhana yang melibatkan tinju, sehingga metode saya akan menang. Namun saya tidak tahu apa syarat untuk membuat TrySZReverse gagal.
aku

Ternyata tidak tercepat dalam semua kasus :), saya memperbarui posting saya. Ini masih perlu diuji dengan unicode untuk kebenaran dan kecepatan.
Sam Saffron

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

Keluaran

Untuk ukuran: 10

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

Untuk ukuran: 100

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

Untuk ukuran: 1000

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

Untuk ukuran: 10000

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

1
Perlu memeriksa string kosong di Reverse(...). Kalau tidak, kerja bagus.
Lara


4

Solusi berbasis tumpukan.

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

Atau

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

4

Harus mengajukan contoh rekursif:

private static string Reverse(string str)
{
    if (str.IsNullOrEmpty(str) || str.Length == 1)
        return str;
    else
        return str[str.Length - 1] + Reverse(str.Substring(0, str.Length - 1));
}

1
string Panjang 0 tidak ditangani
bohdan_trotsenko

Ini tidak berguna.
user3613932

3

Bagaimana tentang:

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

Memiliki masalah codepoint yang sama dengan metode lain di atas dan akan melakukan jauh lebih lambat daripada ketika melakukan yang ToCharArraypertama. Enumerator LINQ juga lebih lambat dari pada Array.Reverse().
Abel

3

Saya telah membuat porta C # dari Microsoft.VisualBasic.Strings . Saya tidak yakin mengapa mereka menyimpan fungsi-fungsi yang berguna (dari VB) di luar System.String in Framework, tetapi masih di bawah Microsoft.VisualBasic. Skenario yang sama untuk fungsi keuangan (misalnya 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();
}

+1, tambahan yang bagus! Saya baru saja mencobanya dengan string s = "abo\u0327\u0307\u035d\U0001d166cd", yang berisi surat odiikuti oleh 3 menggabungkan tanda-tanda diakritik di BMP dan satu tanda menggabungkan (MUSICAL SIMBOL COMBINING STEM) dari pesawat astral (non-BMP) dan itu membuat mereka tetap utuh. Tetapi metode ini lambat jika karakter tersebut hanya muncul di akhir string panjang, karena harus pergi dua kali ke seluruh array.
Abel

3

Maaf karena memposting di utas lama ini. Saya berlatih beberapa kode untuk wawancara.

Inilah yang saya buat untuk C #. Versi pertama saya sebelum refactoring mengerikan.

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

Berbeda dengan Array.Reversemetode di bawah ini, ini muncul lebih cepat dengan 12 karakter atau kurang di string. Setelah 13 karakter, Array.Reversepermulaan menjadi lebih cepat, dan pada akhirnya mendominasi kecepatan. Saya hanya ingin menunjukkan kira-kira di mana kecepatan mulai berubah.

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

    Array.Reverse(charA);

    return new String(charA);
}

Pada 100 karakter dalam string, ini lebih cepat daripada versi saya x 4. Namun, jika saya tahu bahwa string akan selalu kurang dari 13 karakter, saya akan menggunakan yang saya buat.

Pengujian dilakukan dengan Stopwatchdan iterasi 500.000. Juga, saya tidak yakin apakah versi saya menangani Pengganti atau situasi karakter gabungan dengan Unicodepenyandian.


2

"Cara yang lebih baik" tergantung pada apa yang lebih penting bagi Anda dalam situasi, kinerja, keanggunan, pemeliharaan, dll.

Bagaimanapun, inilah pendekatan menggunakan Array. Revers:

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

string reversed = new string(charArray);

2

Jika pernah muncul dalam sebuah wawancara dan Anda diberitahu bahwa Anda tidak dapat menggunakan Array. Dibalik, saya pikir ini mungkin salah satu yang tercepat. Itu tidak membuat string baru dan hanya mengulangi lebih dari setengah dari array (yaitu O (n / 2) iterasi)

    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
Saya cukup yakin stringToReverse.ToCharArray () panggilan akan menghasilkan waktu eksekusi O (N).
Marcel Valdez Orozco

Dalam notasi Big-O , faktor yang tidak bergantung pada x, atau dalam kasus Anda n, tidak digunakan. Algoritme Anda memiliki kinerja f(x) = x + ½x + C, di mana C adalah beberapa konstan. Karena keduanya Cdan faktornya tidak bergantung pada x, algoritma Anda O(x). Itu tidak berarti bahwa itu tidak akan lebih cepat untuk setiap input panjang x, tetapi kinerjanya secara linear tergantung pada panjang input. Untuk menjawab @MarcelValdezOrozco, ya, itu juga O(n), meskipun ia menyalin per potongan 16-byte untuk meningkatkan kecepatan (tidak menggunakan lurus memcpypada total panjang).
Abel

2

Jika Anda memiliki string yang hanya berisi karakter ASCII, Anda dapat menggunakan metode ini.

    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

Pertama-tama apa yang harus Anda pahami adalah bahwa str + = akan mengubah ukuran memori string Anda untuk membuat ruang untuk 1 karakter tambahan. Ini baik-baik saja, tetapi jika Anda memiliki, katakanlah, sebuah buku dengan 1000 halaman yang ingin Anda balikkan, ini akan sangat lama untuk dieksekusi.

Solusi yang mungkin disarankan beberapa orang adalah menggunakan StringBuilder. Apa yang dilakukan pembuat string ketika Anda menjalankan + = adalah bahwa ia mengalokasikan potongan memori yang jauh lebih besar untuk menampung karakter baru sehingga tidak perlu melakukan realokasi setiap kali Anda menambahkan char.

Jika Anda benar-benar menginginkan solusi cepat dan minimal, saya sarankan yang berikut ini:

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

Dalam solusi ini ada satu alokasi memori awal ketika char [] diinisialisasi dan satu alokasi ketika konstruktor string membangun string dari array char.

Di sistem saya, saya menjalankan tes untuk Anda yang membalik string 2 750 000 karakter. Berikut adalah hasil untuk 10 eksekusi:

StringBuilder: kutu 190K - 200K

Array Array: 130K - 160K ticks

Saya juga menjalankan tes untuk String normal + = tetapi saya meninggalkannya setelah 10 menit tanpa output.

Namun, saya juga memperhatikan bahwa untuk string yang lebih kecil, StringBuilder lebih cepat, jadi Anda harus memutuskan implementasi berdasarkan input.

Bersulang


tidak bekerja untuk😀Les Misérables
Charles

@ Charles Ah ya ada batasan yang ditetapkan char kurasa.
Reasurria

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

1
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);
        }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.