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

Мне только что пришлось написать функцию реверса строки в 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;
}

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

Это удивительно сложно, если вам нужна надлежащая международная поддержка. Пример: в хорватском / сербском есть двухсимвольные буквы lj, nj и т. д. Правильная обратная сторона слова ljudi - это idulj, а НЕ idujl. Я уверен, что с арабским, тайским и т. д. Дела будут намного хуже.

dbkk 14.11.2009 13:46

Интересно, медленнее ли объединить строку вместо инициализации временного массива и сохранения результатов в нем, а затем, наконец, преобразования его в строку?

The Muffin Man 01.06.2013 09:29

Более новая связанная тема: Переверните строку с акцентными символами?

Jeppe Stig Nielsen 23.07.2013 19:15

Этот вопрос можно улучшить, определив, что вы имеете в виду под словом «лучший». Самый быстрый? Самый читаемый? Самый надежный в различных крайних случаях (нулевые проверки, несколько языков и т. д.)? Наиболее удобны в обслуживании в разных версиях C# и .NET?

hypehuman 15.04.2016 17:23
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
476
4
725 922
49
Перейти к ответу Данный вопрос помечен как решенный

Ответы 49

Взгляните на запись в Википедии здесь. Они реализуют метод расширения 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);
}

Это замечательно, за исключением того, что методы расширения не были введены в C# 2.0.

Kobi 07.07.2009 16:40

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

Разве сохранение text.Length в переменной не даст немного больше скорости, когда вы ссылаетесь на это через объект?

David Robbins 23.10.2008 05:59
Ответ принят как подходящий
public static string Reverse( string s )
{
    char[] charArray = s.ToCharArray();
    Array.Reverse( charArray );
    return new string( charArray );
}

Я отметил это как правильный ответ, потому что он самый быстрый и был первым, опубликованным с этим решением. Есть отличный материал, из которого можно поучиться в других ответах на этот вопрос.

Guy 23.10.2008 07:45

sambo99: не нужно упоминать Unicode: символы в C# - это символы Unicode, а не байты. Xor может быть быстрее, но помимо того, что он гораздо менее читабелен, это может быть даже то, что Array.Reverse () использует для внутренних целей.

Nick Johnson 23.10.2008 17:18

@Arachnid: На самом деле символы в C# - это кодовые единицы UTF-16; требуется два из них, чтобы представить дополнительный персонаж. См. jaggersoft.com/csharp_standard/9.4.1.htm.

Bradley Grainger 23.10.2008 19:18

Да sambo99 Полагаю, вы правы, но UTF-32 используется довольно редко. И XOR работает быстрее только для очень небольшого диапазона значений, правильным ответом было бы реализовать разные методы для разной длины, я полагаю. Но это ясно и кратко, что, на мой взгляд, является преимуществом.

PeteT 08.12.2008 18:52

Я использую это как одну из многих головоломок, когда мы проверяем стажеров. Спасибо!

410 28.09.2009 19:13

Управляющие символы Unicode делают этот метод бесполезным для нелатинских наборов символов. См. Объяснение Джона Скита с использованием марионетки из носка: codeblog.jonskeet.uk/2009/11/02/… (1/4 пути вниз) или видео: vimeo.com/7516539

Callum Rogers 20.04.2010 03:14

@Callum Спасибо за это, я не знал о дополнительных сложностях управляющих символов Unicode. Кажется, они причиняют боль.

PeteT 20.04.2010 04:20

Надеюсь, вы не встретите суррогатов или комбинирующих персонажей.

dalle 14.10.2010 23:04

Код Джона Скита не работает, как он говорит. Les miserables правильно меняет направление. Скит провалился. Невероятный.

Andrei Rînea 10.12.2012 20:08

@AndreiRinea См. Мой недавний комментарий по вопросу Переверните строку с акцентными символами?.

Jeppe Stig Nielsen 23.07.2013 19:17

@CallumRogers: Извините за придирки, но это не «нелатинские наборы символов» - многие языки охватываются базовым 16-битным диапазоном (A.K.A «базовая многоязычная плоскость» (глянь сюда)). Эти языки включают иврит, арабский и даже китайский и японский (что удивительно). Это действительно зависит от варианта использования. Например, в моем случае переворачивания иврита без диакритических знаков все вышеперечисленное работает отлично.

Amir Abiri 15.11.2013 12:45

Я счел полезным создать метод расширения для строкового класса из приведенного выше. @PeteT, возможно, вы могли бы упомянуть это в ответе, чтобы другие люди могли его найти. используя Систему; публичный статический класс StringExtension {публичная статическая строка Reverse (эта строка) {char [] charArray = s.ToCharArray (); Массив.Реверс (charArray); вернуть новую строку (charArray); }}

Adam B 28.04.2016 23:28

@BradleyGrainger. Вам действительно стоит процитировать то, на что вы отвечаете. Обратите внимание, что человек, которому вы ответили ("паукообразный"), удалил свой комментарий.

barlop 10.07.2016 15:11

@CallumRogers Вы пишете: «Управляющие символы Unicode делают этот метод бесполезным для нелатинских наборов символов». <- какие управляющие символы юникода? В вашей ссылке на статью Джона Скита даже не упоминается ни слова о управляющих символах. Он даже не говорит слова «контроль». Он говорит о комбинациях символов Unicode, таких как диакритические знаки, они не являются управляющими символами. Такие вещи, как французский острый ударение на букву «е». Управляющие символы - это коды ascii 0–31 и 127. В отличие от акцента на букве «е» с ударением.

barlop 10.07.2016 16:14

@CallumRogers Кроме того, как уже указывалось, латинские и нелатинские символы не имеют к этому никакого отношения. «Комбинированные символы Unicode» могут встречаться как в латыни, так и в других языках, не основанных на латыни.

barlop 10.07.2016 16:19

Можно использовать LINQ для короче: вернуть новую строку (s.Reverse (). ToArray ());

zquanghoangz 17.08.2016 08:22

Эта реализация небезопасна для Unicode. Он будет работать, но может вызывать исключения для некоторых символов Юникода (дополнительный набор)

Jim 02.11.2017 19:30

Это не так. Он прерывается при задании строк с суррогатными парами UTF-16.

Functino 06.04.2018 04:05

Я отозвал 4-я редакция, потому что это не сжатая версия исходной реализации, а совершенно другая реализация.

Theodor Zoulias 15.11.2020 18:13

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

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

Это не возвращает строку - вам нужно обернуть это в вызове "new String (...)"

Greg Beech 23.10.2008 04:43

Кстати ... Я только что взглянул на реализацию Array.Reverse, и это сделано наивно для символов ... это должно быть намного быстрее, чем параметр StringBuilder.

Sam Saffron 23.10.2008 04:46

Как мило с твоей стороны, Грег, остановить Самбо и прийти к лучшему решению вместо того, чтобы отрицать его голос.

DOK 23.10.2008 04:47

@ dok1 - не упоминайте об этом :) @ sambo99 - теперь я заинтригован, завтра придется достать профилировщик кода и посмотреть!

Greg Beech 23.10.2008 04:50

Хороший пост, думаю, я бы придерживался своего ответа Array.Reverse не только потому, что он, кажется, имеет хорошую производительность при длине строк, но и потому, что он краток в коде. Давайте посмотрим правде в глаза, обслуживание - это половина проблемы. Также каков штраф производительности для всех, кто использует операторы.

PeteT 23.10.2008 06:52

Я добавил реверс на месте с указателями внизу, если вам интересно. Я не хотел добавлять это в этот пост, потому что этого никогда не следует делать, просто ради любопытства.

Greg Beech 23.10.2008 06:54

Эти методы не обрабатывают строки, содержащие символы вне базовой многоязычной плоскости, то есть символы Unicode> = U + 10000, которые представлены двумя символами C#. Я опубликовал ответ, который правильно обрабатывает такие строки.

Bradley Grainger 23.10.2008 07:42

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

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

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


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

Почему голосование против? Не спорю, но лучше учусь на своих ошибках.

Mike Two 27.07.2011 16:38

Не справляется с объединением кодовых точек среди многих других вещей.

Mooing Duck 02.03.2013 03:16

@MooingDuck - спасибо за объяснение, но я не знаю, что вы подразумеваете под кодовыми точками. Также не могли бы вы подробнее рассказать о «многом другом».

Mike Two 02.03.2013 05:21

@MooingDuck Я поискал кодовые точки. да. Ты прав. Он не обрабатывает кодовые точки. Трудно определить все требования для такого простого вопроса. Спасибо за ответ

Mike Two 02.03.2013 05:28

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

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

строка длины 0 не обрабатывается

bohdan_trotsenko 15.05.2013 18:07

Это бесполезно.

user3613932 11.08.2019 07:19

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

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

Я добавил похожее сравнение в свой пост, это вики сообщества, так что вы должны иметь возможность редактировать. Производительность действительно зависит от длины строки, а также от алгоритма, было бы интересно построить график. Я все еще думаю, что Array.Reverse будет самым быстрым во всех случаях ...

Sam Saffron 23.10.2008 05:43

«будет самым быстрым во всех случаях», когда волшебная функция TrySZReverse (она используется в реализации Reverse) не работает, Array.Reverse откатывается к простой реализации, включающей бокс, так что мой метод выиграет. Однако я не знаю, что является условием отказа TrySZReverse.

aku 23.10.2008 06:03

Оказывается, не во всех случаях он самый быстрый :), я обновил свой пост. Это все еще нужно протестировать с юникодом на правильность и скорость.

Sam Saffron 23.10.2008 06:49

Если вы хотите сыграть в действительно опасную игру, то это, безусловно, самый быстрый способ (примерно в четыре раза быстрее, чем метод 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;
    }
}

Я почти уверен, что это вернет неправильные результаты для строк utf16, это действительно вызывает проблемы :)

Sam Saffron 23.10.2008 07:07

Привет, вы должны дать ссылку на этот пост на этом stackoverflow.com/questions/229346/…, как я уже сказал, это действительно вызывает проблемы ...

Sam Saffron 23.10.2008 15:53

Это может быть совершенно злым и опрометчивым (как вы сами признаете), но все еще существует высокопроизводительный способ перевернуть строку с использованием кода unsafe, который во многих случаях не evil и по-прежнему превосходит Array.Reverse. Взгляните на мой ответ.

Dan Tao 15.06.2010 22:33

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

В следующем примере кода будет правильно преобразована строка, содержащая символы, отличные от BMP, например "\ U00010380 \ U00010381" (угаритская буква альпа, угаритская буква бета).

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

символы в C# - это не байты, это фактические символы. Таким образом, все это совершенно не нужно.

Nick Johnson 23.10.2008 17:20

Фактически, символы в C# - это 16-битные кодовые единицы UTF-16; дополнительный символ кодируется с использованием двух из них, поэтому это необходимо,

Bradley Grainger 23.10.2008 19:14

Для получения дополнительной информации см. §9.4.1.2 спецификации языка C#: jaggersoft.com/csharp_standard/9.4.1.htm

Bradley Grainger 23.10.2008 19:17

Похоже, что System.String действительно должен предоставлять свойство HereBeDragons для строк, содержащих дополнительные символы Unicode.

Robert Rossney 24.10.2008 00:54

Хотя ваше решение лучше других, оно не учитывает комбинирование символов.

Sebastian Negraszus 06.11.2012 17:15

@SebastianNegraszus: Это правильно: этот метод просто меняет кодовые точки в строке. Обратное изменение кластеры графема, вероятно, было бы более «полезным» в целом (но какой «смысл» в обращении произвольной строки в первую очередь?), Но его нелегко реализовать с помощью только встроенных методов в .NET Framework.

Bradley Grainger 06.11.2012 18:38

Для обнаружения кластеров графем CharUnicodeInfo.GetUnicodeCategory дает вам большую часть необходимой информации (в частности, обнаружение комбинированных кодовых точек). Он также сообщит вам, является ли кодовая точка суррогатом, поэтому вам не нужно жестко кодировать диапазоны кодовых точек :-)

Richard 05.02.2013 13:34

@Richard: Правила разбиения кластеров графем немного сложнее, чем просто обнаружение объединения кодовых точек; см. документацию по Границы кластера графемы в UAX # 29 для получения дополнительной информации.

Bradley Grainger 05.02.2013 20:56

Очень хорошая информация! Есть ли у КТО-НИБУДЬ неудачный тест для теста Array.Reverse? И под тестом я подразумеваю образец строки, а не весь модульный тест ... Это действительно помогло бы мне (и другим) убедить разных людей в этой проблеме ..

Andrei Rînea 02.07.2013 19:44

@ Андрей попробуй "Les Mise\u0301rables".

R. Martinho Fernandes 28.10.2013 18:58

Хорошая и безопасная альтернатива этому подходу - использовать StringInfo.GetTextElementEnumerator, который автоматически позаботится о суррогатных парах. Чтобы также позаботиться об объединении символов (которых может быть несколько подряд, и каждый сам может быть суррогатной парой), используйте UnicodeCategory.NonSpacingMark, UnicodeCategory.SpacingCombiningMark и UnicodeCategory.EnclosingMark. Также обратите внимание, что UnicodeCategory.ModifierSymbol можно использовать для символов окружающий. Переворачивание строки действительно нетривиально.

Abel 05.05.2015 18:48
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));
}

Он выполняет свою работу, но, вероятно, это худший способ перевернуть строку из-за выделения памяти и рекурсии String obj МНОГО.

Vinigas 08.01.2019 16:47

Грег Бич опубликовал опцию 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, когда строки становятся больше. Однако для струн малого и среднего размера этот метод сложно превзойти.

StackOverflow для больших строк.

Raz Megrelidze 28.01.2014 23:11

@rezomegreldize: Ага, так и будет;)

Dan Tao 28.01.2014 23:17

Как насчет:

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

Имеет те же проблемы с кодом, что и другие методы, описанные выше, и будет работать намного медленнее, чем при первом выполнении ToCharArray. Перечислитель LINQ также намного медленнее, чем Array.Reverse().

Abel 06.05.2015 13:44

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

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

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

Это правильно обрабатывает только суррогатов. Это мешает совмещать метки: ideone.com/yikdqX.

R. Martinho Fernandes 04.04.2013 01:52

Если это когда-либо всплывало в интервью, и вам сказали, что вы не можете использовать 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);
    }

Я почти уверен, что вызов stringToReverse.ToCharArray () приведет к времени выполнения O (N).

Marcel Valdez Orozco 08.09.2012 09:35

В Обозначение Big-O коэффициент, не зависящий от x, или в вашем случае n, не используется. Ваш алгоритм имеет производительность f(x) = x + ½x + C, где C - некоторая константа. Поскольку и C, и фактор не зависят от x, ваш алгоритм - O(x). Это не означает, что он не будет быстрее для любого ввода длины x, но его производительность линейно зависит от длины ввода. Чтобы ответить @MarcelValdezOrozco, да, это тоже O(n), хотя он копирует по 16-байтовым фрагментам для повышения скорости (он не использует прямой memcpy на общей длине).

Abel 06.05.2015 15:11
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);
        }
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

Необходимо проверить наличие пустой строки в Reverse(...). В остальном - хорошая работа.

Lara 07.01.2020 00:37
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);
        }

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

+1 Один из очень немногих правильных ответов, и много более элегантный и перспективный, чем любой другой, IMO

sehe 27.02.2013 16:29

Однако это не работает для некоторых вещей, зависящих от локали.

R. Martinho Fernandes 27.02.2013 16:46

М-м-м. Я предполагаю, что это все еще будущее (при условии, что это ограничение реализации BCL? Исправления этого автоматически улучшат использование этих API)

sehe 27.02.2013 17:06

Забавно, как большинство других респондентов пытаются сбрить мс с того, что в противном случае является неправильным подходом. Как представительно.

G. Stoynev 05.12.2013 23:38

Фактически значительно быстрее создать экземпляр StringInfo (s), затем выполнить итерацию через SubstringByTextElements (x, 1) и построить новую строку с помощью StringBuilder.

user502255 09.07.2016 07:03

Немного странно, что вы использовали пример Джона Скита, который он привел несколько лет назад codeblog.jonskeet.uk/2009/11/02/… Les Misérables (хотя Джон не упомянул решение, он просто перечислил проблемы). Хорошо, что вы придумали решение. Возможно, Джон Скит изобрел машину времени, вернулся в 2009 год и опубликовал пример проблемы, которую вы использовали в своем решении.

barlop 10.07.2016 14:55

Кроме того, вы можете значительно упростить свой пример, если скажете «lés» (это 3 буквы и одна буква e, и легко увидеть, что происходит). вместо того, чтобы включать слово «несчастные», так как кто-то может случайно взглянуть на «е» в слове «несчастные», которое также имеет «s» на нем и на другой стороне.

barlop 10.07.2016 16:11

Кстати, даже этот код не поддерживает составные смайлы, такие как ?‍?‍?‍? и ??.

xanatos 07.06.2018 10:00

@xanatos, это ошибка, которую вы можете зарегистрировать, потому что да, она должна работать и для них. Похоже, реализация застряла в семантике старой версии Unicode.

R. Martinho Fernandes 08.06.2018 23:39

@ R.MartinhoFernandes Это уже на coreclr проблемы

xanatos 09.06.2018 01:04

Поддержка составных смайлов была добавлена ​​в .NET 5 (docs.microsoft.com/en-us/dotnet/core/compatibility/…)

ckuri 13.11.2020 19:12

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

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

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

Marcel Valdez Orozco 15.05.2013 19:54

На самом деле это не так, поскольку вы создаете new string

mbadawi23 08.09.2018 19:52

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

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

Заметки:

  • не самая быстрая версия - по Мартин Нидерл в 5,7 раза медленнее, чем самый быстрый вариант здесь.
  • этот код, как и многие другие варианты, полностью игнорирует всевозможные многосимвольные комбинации, поэтому ограничьте использование домашними заданиями и строками, которые не содержат такие символы. См. Другой отвечать в этом вопросе для реализации, которая правильно обрабатывает такие комбинации.

Это примерно в 5,7 раза медленнее, чем самая популярная версия, поэтому я бы не рекомендовал ее использовать!

Martin Niederl 06.05.2017 16:42

Не самое быстрое решение, но полезно как однострочное.

adrianmp 18.10.2017 17:19
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));
    }
}
Enumerable.Reverse(input) невероятно похож на input.Reverse()
fubo 17.05.2018 14:41
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;
    }
}

пожалуйста, объясните ответ :)

Saghir A. Khatri 13.12.2013 14:49

Это ужасно неэффективно из-за большого количества конкатенаций строк. Вместо этого попробуйте использовать StringBuilder.

Raz Megrelidze 19.02.2014 16:00

Стековое решение.

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

Если у вас есть строка, содержащая только символы 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);
    }
    string original = "Stack Overflow";
    string reversed = new string(original.Reverse().ToArray());

Это дубликат нескольких других ответов на этой странице.

BradleyDotNET 12.03.2014 23:24

SELECT REVERSE('somestring'); Сделанный.

Откуда ОП сказал, что он пришел из SQL? Или какой тип сервера? этот не ANSI SQL ..

Stuart.Sklinar 27.11.2014 15:19

Я сделал порт 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();
}

+1, приятное дополнение! Я только что попробовал это с string s = "abo\u0327\u0307\u035d\U0001d166cd", который содержит букву o, за которой следуют 3 объединяющих диакритических знака в BMP и один объединяющий знак (MUSICAL SYMBOLING STEM) с астрального плана (не BMP), и он сохраняет их нетронутыми. Но этот метод работает медленно, если такие символы появляются только в конце длинной строки, так как он должен дважды проходить по всему массиву.

Abel 06.05.2015 15:44

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

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

StringBuilder: 190–200 тыс. Тиков

Массив символов: 130–160 тыс. Тиков

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

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

Ваше здоровье

@Charles А, да, я полагаю, есть ограничение на набор символов.

Reasurria 21.02.2020 11:58

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

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

и вот результат:

string Answer = "12345".Inverse(); // = "54321"
Reverse() и ToArray() находятся в неправильном порядке в вашем примере кода.
Chris Walsh 24.02.2017 03:30

Какой цели служит @?

user5389726598465 03.07.2018 11:50

@ user5389726598465 См. эту ссылку: docs.microsoft.com/en-us/dotnet/csharp/language-reference/… Поскольку 'base' - это ключевое слово в C#, оно должно иметь префикс @, чтобы компилятор C# интерпретировал его как идентификатор.

Dyndrilliac 10.05.2019 11:02

Переверните строку, даже не используя новую строку. Скажем так

String input  = "Mark Henry";
//Just to convert into char array. One can simply take input in char array.
Char[] array = input.toCharArray(input);
int a = input.length;

for(int i=0; i<(array.length/2 -1) ; i++)
{
    array[i] = array[i] + array[a];
    array[a] = array[i] - array[a];
    array[i] = array[i] - array[a--];
}

Вы это вообще не тестировали.

weston 13.01.2015 17:24

Извините за публикацию в этой старой теме. Я отрабатываю код для собеседования.

Это то, что я придумал для 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. Однако, если бы я знал, что строки всегда будут меньше 13 символов, я бы использовал тот, который я сделал.

Тестирование проводилось с Stopwatch и 5000000 итераций. Кроме того, я не уверен, обрабатывает ли моя версия суррогаты или комбинированные символьные ситуации с кодировкой Unicode.

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

Есть несколько способов перевернуть струну, я показал 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;
    }

- Использование только массива символов

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

Мне задавали аналогичный вопрос в интервью. Это был мой ответ, хотя, вероятно, он не так быстр, как другие ответы. Мой вопрос был сформулирован как «Создайте класс, который может иметь метод для печати строки в обратном направлении»:

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

Вот так просто:

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

См. выход.

Имейте в виду, что с помощью этого метода вы создаете x.Length, а затем новый объект stringx1, поскольку вы не принимаете во внимание неизменность, присущую string.

Wim Ombelets 31.07.2017 13:11

«Лучшее» может зависеть от многих вещей, но вот еще несколько коротких альтернатив, упорядоченных от быстрого к медленному:

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

вот версия функции, безопасная для юникода, написанная как расширение, которое безопасно обрабатывает юникод. Он близок к отмеченному полному ответу, но не будет вызывать исключения для «Недопустимый высокий суррогатный символ».

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

    }
}

Как насчет использования подстроки

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

может захотеть изменить его на sub = sub + text.Substring(i,1); и вместо записи на консоль вернуть sub после цикла for.

hellyale 14.02.2018 23:28

ааа, спасибо - отредактировал - я пытался быстро показать это на консоли

joegreentea 14.02.2018 23:37

Это код, используемый для обратной строки

public Static void main(){
    string text = "Test Text";
    Console.Writeline(RevestString(text))
}

public Static string RevestString(string text){
    char[] textToChar = text.ToCharArray();
    string result= string.Empty;
    int length = textToChar .Length;
    for (int i = length; i > 0; --i)
    result += textToChar[i - 1];
    return result;
}

Пожалуйста, не публикуйте просто фрагмент кода, опишите, что он делает и почему вы выбрали именно этот подход.

Karl-Henry Martinsson 14.05.2018 23:00

Разве это не в основном идентично коду, который, по словам OP, он ищет лучший способ сделать?

d219 17.05.2018 17:34

Это очень просто

static void Reverse()
    {
        string str = "PankajRawat";
        var arr = str.ToCharArray();
        for (int i = str.Length-1; i >= 0; i--)
        {
            Console.Write(arr[i]);
        }
    }

Самый простой способ:

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

Я использую то же предложение

VhsPiceros 24.07.2019 06:38
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();
}

Начиная с .NET Core 2.1 появился новый способ перевернуть строку с помощью метода string.Create.

Обратите внимание, что это решение некорректно обрабатывает объединяющие символы Unicode и т. д., Так как «Les Mise \ u0301rables» будет преобразовано в «selbarésiM seL». другие ответы для лучшего решения.

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: один как буфер, а другой как внутренний массив строки.

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

string.Create по сути позволяет нам манипулировать внутренним массивом во время создания строки. То есть нам больше не нужен буфер, и поэтому мы можем избежать выделения этого единственного массива символов.

Стив Гордон написал об этом более подробно здесь. Также есть статья о MSDN.

Как использовать string.Create?

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

Метод принимает три параметра:

  1. Длина создаваемой строки,
  2. данные, которые вы хотите использовать для динамического создания новой строки,
  3. и делегат, который создает последнюю строку из данных, где первый параметр указывает на внутренний массив char новой строки, а второй - данные (состояние), которые вы передали в string.Create.

Внутри делегата мы можем указать, как новая строка создается из данных. В нашем случае мы просто копируем символы входной строки в Span, используемый новой строкой. Затем мы переворачиваем Span и, следовательно, переворачиваем всю строку.

Контрольные точки

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

Вот результаты на моей машине:

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

Это намного быстрее, чем обратный Linq

code4j 07.10.2019 20:58
     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();
        }
    }

  }

Использование функции агрегирования LINQ

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

Это очень медленно для больших струн.

Hoodlum 29.01.2020 00:33

Поскольку мне нравится пара ответов - один для использования string.Create и, следовательно, высокой производительности и низкого распределения, а другой для правильности - с использованием класса StringInfo, я решил, что необходим комбинированный подход. Это лучший способ переворота строки :)

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

Обрабатывает все типы символов Юникода

используя System.Globalization;

    public static string ReverseString(this string content) {

        var textElementEnumerator = StringInfo.GetTextElementEnumerator(content);

        var SbBuilder = new StringBuilder(content.Length);

        while (textElementEnumerator.MoveNext()) {
            SbBuilder.Insert(0, textElementEnumerator.GetTextElement());
        }

        return SbBuilder.ToString();
    }

Если кто-то спросит о реверсе строки, цель может заключаться в том, чтобы узнать, знаете ли вы какую-либо побитовую операцию, такую ​​как XOR. В C# у вас есть функция Array.Reverse, однако вы можете использовать простую операцию XOR в нескольких строках кода (минимально)

    public static string MyReverse(string s)
    {
        char[] charArray = s.ToCharArray();
        int bgn = -1;
        int end = s.Length;
        while(++bgn < --end)
        {
            charArray[bgn] ^= charArray[end];
            charArray[end] ^= charArray[bgn];
            charArray[bgn] ^= charArray[end];
        }
        return new string(charArray);
    }

Другие вопросы по теме