Лучший способ перевернуть строку

Мне просто нужно было написать функцию обратного преобразования строк в 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;
}

Лично я не без ума от этой функции и уверен, что есть лучший способ сделать это. Есть?

23.10.2008 00:31:32
Удивительно сложно, если вы хотите надлежащей международной поддержки. Пример: хорватский / сербский имеют двухсимвольные буквы lj, nj и т. Д. Правильным обратным выражением «ljudi» является «idulj», а не «idujl». Я уверен, что вам будет намного хуже, если говорить о арабском, тайском и т. Д.
dbkk 14.11.2009 10:46:52
Интересно, медленнее ли конкатировать строку вместо инициализации временного массива и сохранения результатов в нем, а затем, наконец, преобразовать это в строку?
The Muffin Man 1.06.2013 05:29:45
Гораздо более новая связанная тема: перевернуть строку с символами ударения?
Jeppe Stig Nielsen 23.07.2013 15:15:03
Этот вопрос можно улучшить, определив, что вы подразумеваете под «лучшим». Самый быстрый? Самый читаемый? Наиболее надежны в различных случаях (нулевые проверки, несколько языков и т. Д.)? Больше всего поддерживается в версиях C # и .NET?
hypehuman 15.04.2016 14:23:36
30 ОТВЕТОВ
РЕШЕНИЕ
public static string Reverse( string s )
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}
599
4.11.2017 19:32:19
sambo99: не нужно упоминать юникод: символы в C # являются символами юникода, а не байтами. Xor может быть быстрее, но помимо того, что он гораздо менее читабелен, это может быть даже то, что Array.Reverse () использует для внутреннего использования.
Nick Johnson 23.10.2008 13:18:49
@Arachnid: На самом деле символы в C # - это кодовые единицы UTF-16; требуется два из них, чтобы представить дополнительный характер. См. Jaggersoft.com/csharp_standard/9.4.1.htm .
Bradley Grainger 23.10.2008 15:18:12
Да, sambo99 Полагаю, вы правы, но использовать UTF-32 довольно редко. И XOR быстрее только для очень небольшого диапазона значений, правильный ответ - реализовать разные методы для разных длин, я полагаю. Но это ясно и кратко, что является преимуществом, на мой взгляд.
PeteT 8.12.2008 15:52:25
Управляющие символы Unicode делают этот метод бесполезным для нелатинских наборов символов. См. Объяснение Джона Скита с использованием куклы-носка: codeblog.jonskeet.uk/2009/11/02/… (1/4 пути вниз) или видео: vimeo.com/7516539
Callum Rogers 19.04.2010 23:14:19
Надеюсь, вы не столкнетесь с суррогатами или сочетанием персонажей.
dalle 14.10.2010 19:04:35

Посмотрите на запись в Википедии здесь . Они реализуют метод расширения 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);
}
12
23.10.2008 00:58:56
Это замечательно, за исключением того, что методы расширения не были представлены в c # 2.0.
Kobi 7.07.2009 12:40:07

Во-первых, вам не нужно вызывать, ToCharArrayпоскольку строку уже можно проиндексировать как массив символов, так что это сэкономит вам место.

Следующая оптимизация заключается в использовании a 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метод примерно вдвое быстрее, чем приведенный выше, а для более длинных строк разница еще более выражена. Поэтому, учитывая, что Array.Reverseметод является более простым и быстрым, я бы порекомендовал вам использовать его, а не этот. Я оставляю это здесь, чтобы показать, что это не так, как вы должны это делать (к моему большому удивлению!)

11
23.10.2008 01:12:33
Не будет ли сохранение text.Length в переменной немного быстрее, если вы ссылаетесь на него через объект?
David Robbins 23.10.2008 01:59:41

Это оказывается удивительно сложным вопросом.

Я бы порекомендовал использовать 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);
}

Существует второй подход, который может быть быстрее для определенных длин строк, который использует 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);
    }

Примечание. Если вы хотите поддерживать полную кодировку 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 может быть быстрее для коротких строк.

126
23.05.2017 12:34:51
Это не возвращает строку - вам нужно обернуть это в вызове "new String (...)"
Greg Beech 23.10.2008 00:43:56
Кстати, я только что посмотрел на реализацию Array.Reverse, и это сделано наивно для символов ... это должно быть намного быстрее, чем опция StringBuilder.
Sam Saffron 23.10.2008 00:46:52
Как мило с твоей стороны, Грег, помочь Самбо прийти к лучшему решению, а не голосовать за него.
DOK 23.10.2008 00:47:11
@ dok1 - не упоминай об этом :) @ sambo99 - теперь я заинтригован, завтра мне придется выкроить профилировщик кода и посмотреть!
Greg Beech 23.10.2008 00:50:19
Эти методы не обрабатывают строки, содержащие символы вне базовой многоязычной плоскости, то есть символы Unicode> = U + 10000, которые представлены двумя символами C #. Я опубликовал ответ, который правильно обрабатывает такие строки.
Bradley Grainger 23.10.2008 03:42:13

«Лучший путь» зависит от того, что для вас важнее в вашей ситуации, производительности, элегантности, ремонтопригодности и т. Д.

В любом случае, вот подход с использованием 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
23.10.2008 00:41:32

Попробуйте использовать Array.Reverse


public string Reverse(string str)
{
    char[] array = str.ToCharArray();
    Array.Reverse(array);
    return new string(array);
}
10
23.10.2008 00:48:33
Это невероятно быстро.
Michael Stum♦ 23.10.2008 01:03:28
Почему голосование против? Не спорю, но я лучше учусь на своих ошибках.
Mike Two 27.07.2011 12:38:42
Не в состоянии обрабатывать комбинированные кодовые точки среди многих других вещей.
Mooing Duck 1.03.2013 23:16:28
@MooingDuck - спасибо за объяснение, но я не знаю, что вы подразумеваете под кодовыми точками. Также не могли бы вы остановиться на "многих других вещах".
Mike Two 2.03.2013 01:21:37
@MooingDuck Я посмотрел кодовые пункты. Да. Ты прав. Он не обрабатывает кодовые точки. Трудно определить все требования для такого простого вопроса. Спасибо за отзыв
Mike Two 2.03.2013 01:28:03

Пришлось представить рекурсивный пример:

private static string Reverse(string str)
{
    if (str.IsNullOrEmpty(str) || str.Length == 1)
        return str;
    else
        return str[str.Length - 1] + Reverse(str.Substring(0, str.Length - 1));
}
4
6.05.2015 10:46:36
Строка длины 0 не обрабатывается
bohdan_trotsenko 15.05.2013 14:07:03
Это не полезно.
user3613932 11.08.2019 04:19:54

Извините за длинный пост, но это может быть интересно

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: 346мс
  • ReverseUsingArrayClass: 87мс
  • ReverseUsingStringBuilder: 824 мс
  • ReverseUsingStack: 2086мс
  • ReverseUsingXOR: 319мс
5
23.10.2008 01:17:58
Я добавил аналогичное сравнение в свой пост, это вики сообщества, чтобы вы могли редактировать. Производительность действительно зависит от длины строки, а также от алгоритма, было бы интересно на графике. Я все еще думаю, что Array.Reverse будет самым быстрым во всех случаях ...
Sam Saffron 23.10.2008 01:43:25
«будет быстрее всех», когда магическая функция TrySZReverse (она используется в реализации Reverse) не работает, Array.Reverse возвращается к простой реализации, включающей бокс, поэтому мой метод победит. Однако я не знаю, что является условием для сбоя TrySZReverse.
aku 23.10.2008 02:03:04
Оказывается, он не самый быстрый во всех случаях :), я обновил свой пост. Это все еще должно быть проверено с юникодом для правильности и скорости.
Sam Saffron 23.10.2008 02:49:21

Если вы хотите играть в действительно опасную игру, то это самый быстрый способ (примерно в четыре раза быстрее, чем Array.Reverseметод). Это обратный процесс с использованием указателей.

Обратите внимание, что я действительно не рекомендую это для какого-либо использования, ( посмотрите здесь по некоторым причинам, почему вы не должны использовать этот метод ), но просто интересно посмотреть, что это можно сделать, и что строки на самом деле не являются неизменяемыми как только вы включите небезопасный код.

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;
    }
}
14
23.05.2017 12:34:51
Я уверен, что это даст неправильные результаты для строк utf16, это действительно вызывает проблемы :)
Sam Saffron 23.10.2008 03:07:34
Привет, вы должны сослаться на этот пост на этом stackoverflow.com/questions/229346/… , как я уже говорил, это действительно вызывает проблемы ...
Sam Saffron 23.10.2008 11:53:07
Это может быть полностью злым и опрометчивым (как вы сами признаете), но все же есть высокопроизводительный способ перевернуть строку, используя не злой unsafeкод, который все еще бьет во многих случаях. Посмотрите на мой ответ. Array.Reverse
Dan Tao 15.06.2010 18:33:18

Если строка содержит данные 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
23.10.2008 04:08:22
На самом деле, символы в C # являются 16-битными единицами кода UTF-16; дополнительный символ кодируется с использованием двух из них, так что это необходимо,
Bradley Grainger 23.10.2008 15:14:18
Кажется, что System.String действительно должен предоставлять свойство HereBeDragons для строк, которые содержат дополнительные символы Unicode.
Robert Rossney 23.10.2008 20:54:35
@SebastianNegraszus: Это правильно: этот метод просто меняет кодовые точки в строке. Реверсирование кластеров графем , вероятно, было бы более «полезным» в целом (но какова «польза» от реверсирования произвольной строки?), Но это нелегко реализовать с помощью только встроенных методов в .NET Framework.
Bradley Grainger 6.11.2012 14:38:08
@Richard: правила разрушения графемных кластеров немного сложнее, чем просто обнаружение комбинированных кодовых точек; см. документацию по Графикам Grapheme Cluster в UAX # 29 для получения дополнительной информации.
Bradley Grainger 5.02.2013 16:56:58
Очень хорошая информация! Есть ли НИКОМУ есть неудовлетворительный тест для испытания Array.Reverse? И под тестом я подразумеваю образец строки, а не целый модульный тест ... Это действительно помогло бы мне (и другим) убедить разных людей в этом вопросе ..
Andrei Rînea 2.07.2013 15:44:31
public string rev(string str)
{
    if (str.Length <= 0)
        return string.Empty;
    else
        return str[str.Length-1]+ rev(str.Substring(0,str.Length-1));
}
-4
29.06.2011 09:53:23
Это делает свою работу, но это, вероятно, худший способ перевернуть строку из-за выделения памяти String obj MUCH и рекурсии.
Vinigas 8.01.2019 13:47:29

Грег Бич опубликовал unsafeопцию, которая действительно настолько быстра, насколько это возможно (это разворот на месте); но, как он указал в своем ответе, это совершенно катастрофическая идея .

Тем не менее, я удивлен, что так много консенсуса, что Array.Reverseэто самый быстрый метод. Есть еще unsafeподход, который возвращает обратную строку (без изменений на месте) значительно быстрее, чем Array.Reverseметод для маленьких строк:

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
30.04.2013 00:19:44
StackOverflow на больших строках.
Raz Megrelidze 28.01.2014 19:11:54
@rezomegreldize: Да, это произойдет;)
Dan Tao 28.01.2014 19:17:51

Как насчет:

    private string Reverse(string stringToReverse)
    {
        char[] rev = stringToReverse.Reverse().ToArray();
        return new string(rev); 
    }
3
4.02.2011 01:03:52
Имеет те же проблемы с кодами, что и другие методы, описанные выше, и работает намного медленнее, чем при ToCharArrayпервом. Перечислитель LINQ также намного медленнее, чем Array.Reverse().
Abel 6.05.2015 10:44:29

Хорошо, в интересах «не повторяйся» я предлагаю следующее решение:

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

Насколько я понимаю, эта реализация, доступная по умолчанию в VB.NET, правильно обрабатывает символы Юникода.

25
7.12.2011 17:06:20
Это только правильно обрабатывает суррогаты. Это испортило объединение марок: ideone.com/yikdqX .
R. Martinho Fernandes 3.04.2013 21:52:31

Если это когда-нибудь появилось в интервью, и вам сказали, что вы не можете использовать 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
16.02.2012 07:21:35
Я вполне уверен, что вызов stringToReverse.ToCharArray () произведет O (N) время выполнения.
Marcel Valdez Orozco 8.09.2012 05:35:01
В нотации Big-O фактор, не зависящий xили, в вашем случае, nне используется. Ваш алгоритм имеет производительность f(x) = x + ½x + C, где C - некоторая константа. Поскольку оба Cи фактор не зависят от x, ваш алгоритм O(x). Это не означает, что он не будет быстрее для любого ввода длины x, но его производительность линейно зависит от длины ввода. Чтобы ответить на @MarcelValdezOrozco, да, это также так O(n), хотя он копирует на 16-байтовые блоки для повышения скорости (он не использует прямое memcpyна общей длине).
Abel 6.05.2015 12:11:41
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
27.04.2012 07:46:46
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
5
15.05.2013 15:52:34
Необходимо проверить наличие пустой строки в Reverse(...). В остальном хорошая работа.
Lara 6.01.2020 21:37:09
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
22.02.2013 02:09:48

Здесь решение, которое правильно меняет строку "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 для итерации кластера графемы , который был там с тех пор, но, кажется, немного «скрыт».

181
24.07.2013 16:19:10
+1 Один из очень немногих правильных ответов, и намного более элегантный и перспективный, чем любой другой, ИМО
sehe 27.02.2013 12:29:18
Однако это не так для некоторых зависящих от локали вещей.
R. Martinho Fernandes 27.02.2013 12:46:38
Забавно, что большинство других опрошенных пытаются сбить с толку то, что иначе неверно. Как представитель.
G. Stoynev 5.12.2013 19:38:27
На самом деле значительно быстрее создавать экземпляры StringInfo (s), затем выполнять итерацию по SubstringByTextElements (x, 1) и создавать новую строку с помощью StringBuilder.
user502255 9.07.2016 04:03:44
Немного странно, что вы использовали пример Джона Скита, который он привел несколькими годами ранее codeblog.jonskeet.uk/2009/11/02/… Les Misérables (хотя Джон не упомянул о решении, он просто перечислил проблемы). Хорошо, что вы придумали решение. Возможно, Джон Скит изобрел машину времени, вернулся в 2009 г. и опубликовал пример проблемы, который вы использовали в своем решении.
barlop 10.07.2016 11:55:21

Не беспокойтесь о функции, просто сделайте это на месте. Примечание: вторая строка вызовет исключение аргумента в окне Immediate некоторых версий VS.

string s = "Blah";
s = new string(s.ToCharArray().Reverse().ToArray()); 
7
2.04.2013 00:45:52
Какой-то парень нашел время, чтобы понизить рейтинг каждого ответа (включая мой) без объяснения причин.
Marcel Valdez Orozco 15.05.2013 15:54:21
На самом деле это не так, поскольку вы создаетеnew string
mbadawi23 8.09.2018 16:52:49

Если вы можете использовать LINQ (.NET Framework 3.5+), то следуя одному вкладышу, вы получите короткий код. Не забудьте добавить, using System.Linq;чтобы иметь доступ к Enumerable.Reverse:

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

Ноты:

  • не самая быстрая версия - по словам Мартина Нидерла, в 5,7 раза медленнее, чем самый быстрый выбор здесь.
  • этот код, как и многие другие параметры, полностью игнорирует все виды многосимвольных комбинаций, поэтому ограничивайте использование домашними заданиями и строками, которые не содержат таких символов. Посмотрите другой ответ в этом вопросе для реализации, которая правильно обрабатывает такие комбинации.
51
3.02.2020 20:53:09
Это примерно в 5,7 раза медленнее, чем самая популярная версия, поэтому я бы не рекомендовал использовать это!
Martin Niederl 6.05.2017 13:42:05
Не самое быстрое решение, но полезно как однострочник.
adrianmp 18.10.2017 14:19:46
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
9.04.2013 17:47:44
Enumerable.Reverse(input)input.Reverse()
fubo 17.05.2018 11:41:04
string A = null;
//a now is reversed and you can use it
A = SimulateStrReverse.StrReverse("your string");

public static class SimulateStrReverse
{
    public static string StrReverse(string expression)
    {
        if (string.IsNullOrEmpty(expression))
            return string.Empty;

        string reversedString = string.Empty;
        for (int charIndex = expression.Length - 1; charIndex >= 0; charIndex--)
        {
            reversedString += expression[charIndex];
        }
        return reversedString;
    }
}
-1
19.02.2014 09:05:18
Пожалуйста, объясните ответ :)
Saghir A. Khatri 13.12.2013 10:49:58
Это ужасно неэффективно из-за большого количества конкатенаций строк. Попробуйте вместо этого использовать StringBuilder.
Raz Megrelidze 19.02.2014 12:00:57

Стек на основе решения.

    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
17.06.2014 22:35:30

Если у вас есть строка, содержащая только символы 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
29.01.2014 12:19:52
    string original = "Stack Overflow";
    string reversed = new string(original.Reverse().ToArray());
0
12.03.2014 19:15:21
Это дубликат нескольких других ответов на этой странице.
BradleyDotNET 12.03.2014 19:24:13

SELECT REVERSE('somestring'); Выполнено.

-8
14.03.2014 20:32:50
Откуда ОП сказал, что он пришел из SQL? Или какой тип сервера? это не ANSI SQL ..
Stuart.Sklinar 27.11.2014 12:19:56

Я сделал порт C # из Microsoft.VisualBasic.Strings . Я не уверен, почему они хранят такие полезные функции (из VB) вне System.String в Framework, но все еще в Microsoft.VisualBasic. Тот же сценарий для финансовых функций (например 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
30.06.2014 21:31:58
+1, приятное дополнение! Я только что попробовал с этим string s = "abo\u0327\u0307\u035d\U0001d166cd", который содержит букву, oза которой следуют 3 объединяющих диакритических знака в BMP и один объединяющий знак (КОМБИНИРУЮЩИЙ СТУМ МУЗЫКАЛЬНОГО СИМВОЛА) с астрального плана (не-BMP), и он сохраняет их нетронутыми. Но метод медленный, если такие символы появляются только в конце длинной строки, так как он должен пройти дважды по всему массиву.
Abel 6.05.2015 12:44:25

Прежде всего, вы должны понять, что str + = изменит размер вашей строковой памяти, чтобы освободить место для 1 дополнительного символа. Это хорошо, но если у вас есть, скажем, книга с 1000 страницами, которую вы хотите перевернуть, это займет очень много времени.

Решение, которое некоторые люди могут предложить, это использование StringBuilder. Что делает строитель строк, когда вы выполняете + =, так это то, что он выделяет гораздо большие куски памяти для хранения нового символа, так что ему не нужно выполнять перераспределение каждый раз, когда вы добавляете символ.

Если вы действительно хотите быстрое и минимальное решение, я бы предложил следующее:

            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 [], и одно выделение, когда строковый конструктор строит строку из массива char.

В моей системе я провел для вас тест, который переворачивает строку из 2 750 000 символов. Вот результаты для 10 казней:

StringBuilder: 190K - 200K тиков

Char Char Array: 130K - 160K тиков

Я также запустил тест на нормальный тип String + =, но я отказался от него через 10 минут без вывода.

Однако я также заметил, что для небольших строк StringBuilder работает быстрее, поэтому вам придется выбирать реализацию на основе входных данных.

ура

2
9.10.2014 05:51:45
не работает для
Charles 18.02.2020 00:50:42
@ Чарльз А, да, я думаю, есть ограничение набора символов.
Reasurria 21.02.2020 08:58:15

Простой и приятный ответ - использование метода расширения:

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

и вот вывод:

string Answer = "12345".Inverse(); // = "54321"
15
18.07.2016 00:06:57
Reverse()и ToArray()в неправильном порядке в вашем примере кода.
Chris Walsh 24.02.2017 00:30:31
Какой цели служит @?
user5389726598465 3.07.2018 08:50:46
@ user5389726598465 См. эту ссылку: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… Поскольку «base» - это ключевое слово в C #, для того, чтобы компилятор C # интерпретировал его как префикс, он должен начинаться с @ идентификатор.
Dyndrilliac 10.05.2019 08:02:59