Редактировать символы в строке в C # - PullRequest
13 голосов
/ 28 мая 2010

Какой самый чистый способ редактирования символов в строке в C #?

Что такое эквивалент C # в C ++:

std::string myString = "boom";
myString[0] = "d";

Ответы [ 11 ]

30 голосов
/ 28 мая 2010

Используйте взамен StringBuilder.

строка является неизменной, как описано в MSDN :

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

Итак, вы хотите что-то вроде:

 StringBuilder sb = new StringBuilder("Bello World!");
 sb[0] = 'H';
 string str = sb.ToString();
15 голосов
/ 28 мая 2010

Решил время, что я почувствовал, где два наиболее канонических подхода, плюс один, который я добавил, как непредставленный; вот что я нашел (Release build):

ReplaceAtChars: 86ms
ReplaceAtSubstring: 258ms
ReplaceAtStringBuilder: 161ms

Очевидно, что подход массива Char лучше всего оптимизирован во время выполнения. Что фактически говорит о том, что текущий ведущий ответ (StringBuilder), вероятно, не является лучшим ответом.

А вот тест, который я использовал:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("ReplaceAtChars: " + new Stopwatch().Time(() => "test".ReplaceAtChars(1, 'E').ReplaceAtChars(3, 'T'), 1000000) + "ms");
        Console.WriteLine("ReplaceAtSubstring: " + new Stopwatch().Time(() => "test".ReplaceAtSubstring(1, 'E').ReplaceAtSubstring(3, 'T'), 1000000) + "ms");
        Console.WriteLine("ReplaceAtStringBuilder: " + new Stopwatch().Time(() => "test".ReplaceAtStringBuilder(1, 'E').ReplaceAtStringBuilder(3, 'T'), 1000000) + "ms");
    }
}

public static class ReplaceAtExtensions
{
    public static string ReplaceAtChars(this string source, int index, char replacement)
    {
        var temp = source.ToCharArray();
        temp[index] = replacement;
        return new String(temp);
    }

    public static string ReplaceAtStringBuilder(this string source, int index, char replacement)
    {
        var sb = new StringBuilder(source);
        sb[index] = replacement;
        return sb.ToString();
    }

    public static string ReplaceAtSubstring(this string source, int index, char replacement)
    {
        return source.Substring(0, index) + replacement + source.Substring(index + 1);
    }
}

public static class StopwatchExtensions
{
    public static long Time(this Stopwatch sw, Action action, int iterations)
    {
        sw.Reset();
        sw.Start();
        for (int i = 0; i < iterations; i++)
        {
            action();
        }
        sw.Stop();

        return sw.ElapsedMilliseconds;
    }
}
8 голосов
/ 28 мая 2010

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

Вы можете получить символ [] из строки, внести изменения и создать новую строку из измененного массива.

Вы можете использовать такие вещи, как замена, хотя они не дают вам контроль над базой данных как C. Они также будут создавать новую строку с каждым изменением, где стиль char [] позволяет вам вносить все изменения и затем создавать новую строку из нее.

8 голосов
/ 28 мая 2010

Строки в .Net являются неизменяемыми. Это означает, что вы не можете редактировать их. Все, что вы можете сделать, это сделать новые. Вы должны сделать что-то вроде этого:

string myString = "boom";
myString = "d" + myString.Substring(1);
5 голосов
/ 28 мая 2010

Вы не можете, так как строки неизменны.

Вы всегда можете свернуть свой собственный метод расширения, чтобы сделать что-то похожее:

public static string ReplaceAtIndex(this string original, 
    int index, char character)
{
    char[] temp = original.ToCharArray();
    temp[index] = character;
    return new String(temp);
}

И назвать его как:

string newString = myString.ReplaceAtIndex(0, 'd');
4 голосов
/ 28 мая 2010

Как сказал Брайан, используйте StringBuilder вместо:

string myString = "boom";
StringBuilder myStringBuilder = new StringBuilder(myString);
myStringBuilder[0] = 'd';
// Whatever else you're going to do to it

Когда вам снова понадобится строка, позвоните myStringBuilder.ToString()

3 голосов
/ 28 мая 2010
string myString = "boom";
char[] myChars = myString.ToCharArray();
myChars[0] = 'd';
myString = new String(myChars);
3 голосов
/ 28 мая 2010
string myString = "boom";
char[] arr = myString.ToCharArray();
arr[0] = 'd';
myString = new String(arr);
2 голосов
/ 09 июня 2010

Строки неизменны в C #. Используйте класс StringBuilder или массив символов.

StringBuilder sb = new StringBuilder("boom");
sb[0] = 'd';

IIRC, .NET использует так называемое String Pooling. Каждый раз, когда создается новый строковый литерал, он сохраняется в памяти как часть пула строк. Если вы создадите вторую строку, которая соответствует строке в пуле строк, обе переменные будут ссылаться на одну и ту же память.

Когда вы пытаетесь выполнить операцию, аналогичную той, которую вы делали, чтобы заменить символ 'b' на символ '' d ', используя строки в .NET, ваша программа фактически создает вторую строку в пуле строк со значением "doom" Хотя, с вашей точки зрения, это не выглядит так, как будто это вообще происходит. Читая код, можно предположить, что символ заменяется.

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

2 голосов
/ 28 мая 2010

Вот забавный, который я собрал. Пожалуйста, имейте в виду, что это не очень эффективно, особенно для простых замен. Тем не менее, было весело писать и поддается довольно читабельной модели использования. Это также подчеркивает малоизвестный факт, что String реализует IEnumerable.

public static class LinqToStrings
{
    public static IQueryable<char> LinqReplace(this string source, int startIndex, int length, string replacement)
    {
        var querySource = source.AsQueryable();
        return querySource.LinqReplace(startIndex, length, replacement);
    }

    public static IQueryable<char> LinqReplace(this IQueryable<char> source, int startIndex, int length, string replacement)
    {
        var querySource = source.AsQueryable();
        return querySource.Take(startIndex).Concat(replacement).Concat(querySource.Skip(startIndex + length));
    }

    public static string AsString(this IQueryable<char> source)
    {
        return new string(source.ToArray());
    }
}

А вот пример использования:

public void test()
{
    var test = "test";
    Console.WriteLine("Old: " + test);
    Console.WriteLine("New: " + test.LinqReplace(0, 4, "SOMEPIG")
                                    .LinqReplace(4, 0, "terrific")
                                    .AsString());
}

Выходы:

Old: test
New: SOMEterrificPIG

Другая версия того же подхода, которая не так ужасно медленная, проста с использованием подстроки:

public static string ReplaceAt(this string source, int startIndex, int length, string replacement)
{
    return source.Substring(0, startIndex) + replacement + source.Substring(startIndex + length);
}

И в замечательном примере того, почему вы должны профилировать свой код, и почему вы, вероятно, должны не использовать мою реализацию LinqToStrings в рабочем коде, вот тест синхронизации:

Console.WriteLine("Using LinqToStrings: " + new Stopwatch().Time(() => "test".LinqReplace(0, 4, "SOMEPIG").LinqReplace(4, 0, "terrific").AsString(), 1000));
Console.WriteLine("Using Substrings: " + new Stopwatch().Time(() => "test".ReplaceAt(0, 4, "SOMEPIG").ReplaceAt(4, 0, "terrific"), 1000));

Который измеряет такты таймера в 1000 итераций, производя такой вывод:

Using LinqToStrings: 3,818,953
Using Substrings: 1,157
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...