Как заменить часть строки позицией?


155

У меня есть эта строка: ABCDEFGHIJ

Мне нужно заменить из положения 4 в положение 5 строкой ZX

Это будет выглядеть так: ABCZXFGHIJ

Но не использовать с string.replace("DE","ZX")- мне нужно использовать с позиции

Как мне это сделать?


@TotZam - пожалуйста, проверьте даты. Этот старше, чем тот, который вы связали.
— ToolmakerSteve

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

@TotZam - ах, я не знал об этой рекомендации - спасибо за указание на это. (Хотя кажется странным, что что-то более старое сообщается как дубликат чего-то более нового, в таком случае стоило бы явно объяснить, что вы помечаете как дубликат вопроса СТАРЫЕ, потому что у связанного есть лучшие ответы.)
— ToolmakerSteve

Ответы:


198

Самый простой способ добавить и удалить диапазоны в строке - использовать StringBuilder.

var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();

Альтернативой является использование String.Substring, но я думаю, что StringBuilderкод становится более читабельным.


6
было бы лучше обернуть это в методе расширения :)
— balexandre

Почему вы используете StringBuilder здесь? Проверьте ответ V4Vendetta, который является намного более читабельным ...
— Fortega

1
@Fortega Remove () и Insert () каждый создает и возвращает новую строку. Подход StringBuilder позволяет избежать этих дополнительных затрат, работая в заранее выделенном разделе памяти и перемещая вокруг него символы.
— Redcalx

@redcalx Это правда, но дополнительные затраты вводятся за счет введения объекта
— StringBuilder

2
Какой смысл использовать StringBuilder здесь, если вы вызываете Remove и Insert так же, как V4Vendetta, но он делает это прямо в строке? Кажется, избыточный код.
— metaududdy

186
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");

17
Я предпочитаю это, чем инициализировать новый StringBuilder (...). Спасибо!
— Майкл Норгрен

3
Я не знаю, почему кто-то будет использовать stringbuilder для этой операции. Это хороший ответ
— NappingRabbit

43

ReplaceAt (индекс int, длина int, замена строки)

Вот метод расширения, который не использует StringBuilder или Substring. Этот метод также позволяет замещающей строке выходить за пределы длины исходной строки.

//// str - the source string
//// index- the start location to replace at (0-based)
//// length - the number of characters to be removed before inserting
//// replace - the string that is replacing characters
public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return str.Remove(index, Math.Min(length, str.Length - index))
            .Insert(index, replace);
}

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

"0123456789".ReplaceAt(7, 5, "Hello") = "0123456Hello"

В противном случае вы можете указать количество символов, которые будут удалены:

"0123456789".ReplaceAt(2, 2, "Hello") = "01Hello456789"

Если вы укажете, что длина равна 0, то эта функция действует как функция вставки:

"0123456789".ReplaceAt(4, 0, "Hello") = "0123Hello456789"

Я предполагаю, что это более эффективно, поскольку класс StringBuilder не нужно инициализировать и поскольку он использует более простые операции. Пожалуйста, поправьте меня, если я ошибаюсь. :)


5
Stringbuilder более эффективен, если длина строки превышает, скажем, 200 символов.
— Тим Шмельтер,

Нет стресса. Работал прямо. Спасибо
— D_Edet

9

Используйте String.Substring()(подробности здесь ), чтобы вырезать левую часть, затем замену, затем правую часть. Играйте с индексами, пока не поймете как надо :)

Что-то вроде:

string replacement=original.Substring(0,start)+
    rep+original.Substring(start+rep.Length);

2
Я создал три метода, используя три вышеупомянутых метода (string.Remove / Insert, Stringbuilder.Remove / Insert и ответ подстроки Даниэля, и SubString был самым быстрым методом.
— Франсин ДеГруд Тейлор

7

Как метод расширения.

public static class StringBuilderExtension
{
    public static string SubsituteString(this string OriginalStr, int index, int length, string SubsituteStr)
    {
        return new StringBuilder(OriginalStr).Remove(index, length).Insert(index, SubsituteStr).ToString();
    }
}


4

Если вы заботитесь о производительности, то здесь вам нужно избегать распределения. И если вы на .Net Ядра 2.1+ (или, еще не издававшийся, .Net стандарт 2.1), то вы можете, используя в string.Createметод :

public static string ReplaceAt(this string str, int index, int length, string replace)
{
    return string.Create(str.Length - length + replace.Length, (str, index, length, replace),
        (span, state) =>
        {
            state.str.AsSpan().Slice(0, state.index).CopyTo(span);
            state.replace.AsSpan().CopyTo(span.Slice(state.index));
            state.str.AsSpan().Slice(state.index + state.length).CopyTo(span.Slice(state.index + state.replace.Length));
        });
}

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


3

Вы можете попробовать что-нибудь связать это:

string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);

3

Как и другие упомянули, Substring()функция существует по причине:

static void Main(string[] args)
{
    string input = "ABCDEFGHIJ";

    string output = input.Overwrite(3, "ZX"); // 4th position has index 3
    // ABCZXFGHIJ
}

public static string Overwrite(this string text, int position, string new_text)
{
    return text.Substring(0, position) + new_text + text.Substring(position + new_text.Length);
}

Также я рассчитал это против StringBuilderрешения и получил 900 тиков против 875. Так что это немного медленнее.


@ Aaron.S - нет, это не так. В моем примере "ABCDEFGHIJ"и "ZX"разной длины.
— Джон Алексиу


2

С помощью этого поста я создаю следующую функцию с дополнительными проверками длины

public string ReplaceStringByIndex(string original, string replaceWith, int replaceIndex)
{
    if (original.Length >= (replaceIndex + replaceWith.Length))
    {
        StringBuilder rev = new StringBuilder(original);
        rev.Remove(replaceIndex, replaceWith.Length);
        rev.Insert(replaceIndex, replaceWith);
        return rev.ToString();
    }
    else
    {
        throw new Exception("Wrong lengths for the operation");
    }
}

2

Все остальные ответы не работают, если строка содержит символ Unicode (например, Emojis), потому что символ Unicode весит больше байтов, чем символ.

Пример : смайлики '🎶', преобразованные в байты, будут весить эквивалент 2 символов. Таким образом, если символ unicode находится в начале вашей строки, offsetпараметр будет смещен).

В этом разделе я расширяю класс StringInfo до Replace, сохраняя положение алгоритма Ника Миллера, чтобы избежать этого:

public static class StringInfoUtils
{
    public static string ReplaceByPosition(this string str, string replaceBy, int offset, int count)
    {
        return new StringInfo(str).ReplaceByPosition(replaceBy, offset, count).String;
    }

    public static StringInfo ReplaceByPosition(this StringInfo str, string replaceBy, int offset, int count)
    {
        return str.RemoveByTextElements(offset, count).InsertByTextElements(offset, replaceBy);
    }

    public static StringInfo RemoveByTextElements(this StringInfo str, int offset, int count)
    {
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            offset + count < str.LengthInTextElements
                ? str.SubstringByTextElements(offset + count, str.LengthInTextElements - count - offset)
                : ""
            ));
    }
    public static StringInfo InsertByTextElements(this StringInfo str, int offset, string insertStr)
    {
        if (string.IsNullOrEmpty(str?.String))
            return new StringInfo(insertStr);
        return new StringInfo(string.Concat(
            str.SubstringByTextElements(0, offset),
            insertStr,
            str.LengthInTextElements - offset > 0 ? str.SubstringByTextElements(offset, str.LengthInTextElements - offset) : ""
        ));
    }
}

dotnetfiddle.net/l0dqS5 Я не мог заставить другие методы потерпеть неудачу на Unicode. Пожалуйста, измените скрипку, чтобы продемонстрировать ваш вариант использования. Очень заинтересованы в результатах.
— Маркус

1
@MarkusHooge Попробуйте разместить символы Unicode в начале вашей строки, например: dotnetfiddle.net/3V2K3Y . Вы увидите, что работает только последняя строка, и поместите символ на 4-е место (в другом случае, символ unicode занимает длину 2 символа)
— GGO

Вы правы, это не ясно. Я обновил свой ответ, чтобы добавить больше объяснений, почему другие ответы не работают
— GGO

1

Я искал решение со следующими требованиями:

  1. использовать только одно однострочное выражение
  2. использовать только системные встроенные методы (нет пользовательских утилит)

Решение 1

Решение, которое лучше всего подходит мне, это:

// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();

Это использует StringBuilder.Replace(oldChar, newChar, position, count)

Решение 2

Другое решение, которое удовлетворяет моим требованиям, заключается в использовании Substringс конкатенацией:

string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);

Это тоже нормально. Я думаю, что это не так эффективно, как в первом случае с производительностью (из-за ненужной конкатенации строк). Но преждевременная оптимизация - корень всего зла .

Так что выбирайте тот, который вам нравится больше всего :)


Решение 2 работает, но требует исправления: string newString = oldStr.Substring (0, i) + c + oldString.Substring (i + 1, oldString.Length- (i + 1));
— Юра G

0

Лучше использовать String.substr().

Как это:

ReplString = GivenStr.substr(0, PostostarRelStr)
           + GivenStr(PostostarRelStr, ReplString.lenght());

0
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();

Позвольте мне объяснить мое решение.
Учитывая постановку задачи об изменении строки в двух ее конкретных положениях («от позиции 4 до позиции 5») с двумя символами «Z» и «X», и предлагается использовать индекс позиции для изменения строки, а не строки. Replace ( ) метод (может быть из-за возможности повторения некоторых символов в реальной строке), я бы предпочел использовать минималистский подход для достижения цели по сравнению с использованием Substring()и строки Concat()или строки Remove()и Insert()подхода. Хотя все эти решения будут служить цели в достижении той же цели, но это зависит только от личного выбора и философии урегулирования с минималистским подходом.
Возвращаясь к моему решению, упомянутому выше, если мы рассмотрим подробнееstring иStringBuilderоба они внутренне обрабатывают данную строку как массив символов. Если мы посмотрим на реализацию, то StringBuilderона поддерживает внутреннюю переменную, похожую на « internal char[] m_ChunkChars;», для захвата заданной строки. Теперь, поскольку это внутренняя переменная, мы не можем напрямую обращаться к ней. Для внешнего мира, чтобы иметь возможность получить доступ и изменить этот массив символов, StringBuilderпредоставляет их через свойство indexer, которое выглядит примерно так:

    [IndexerName("Chars")]
    public char this[int index]
    {
      get
      {
        StringBuilder stringBuilder = this;
        do
        {
          // … some code
            return stringBuilder.m_ChunkChars[index1];
          // … some more code
        }
      }
      set
      {
        StringBuilder stringBuilder = this;
        do
        {
            //… some code
            stringBuilder.m_ChunkChars[index1] = value;
            return;
            // …. Some more code
        }
      }
    }

Мое решение, упомянутое выше, использует эту возможность индексатора для непосредственного изменения внутреннего символьного массива, который IMO эффективен и минималистичен.

КСТАТИ; мы можем переписать вышеприведенное решение более подробно, например, ниже

 string myString = "ABCDEFGHIJ";
 StringBuilder tempString = new StringBuilder(myString);
 tempString[3] = 'Z';
 tempString[4] = 'X';
 string modifiedString = tempString.ToString();

В этом контексте также хотелось бы упомянуть, что в случае stringэтого также есть свойство indexer как средство для раскрытия своего внутреннего символьного массива, но в этом случае у него есть только свойство Getter (и нет Setter), поскольку строка является неизменяемой по своей природе. И именно поэтому мы должны использовать, StringBuilderчтобы изменить массив символов.

[IndexerName("Chars")]
public extern char this[int index] { [SecuritySafeCritical, __DynamicallyInvokable, MethodImpl(MethodImplOptions.InternalCall)] get; }

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


1
Пожалуйста, измените свой ответ, чтобы объяснить, как этот код отвечает на вопрос
— tshimkus

0
String timestamp = "2019-09-18 21.42.05.000705";
String sub1 = timestamp.substring(0, 19).replace('.', ':'); 
String sub2 = timestamp.substring(19, timestamp.length());
System.out.println("Original String "+ timestamp);      
System.out.println("Replaced Value "+ sub1+sub2);

0

Вот простой метод расширения:

    public static class StringBuilderExtensions
    {
        public static StringBuilder Replace(this StringBuilder sb, int position, string newString)
            => sb.Replace(position, newString.Length, newString);

        public static StringBuilder Replace(this StringBuilder sb, int position, int length, string newString)
            => (newString.Length <= length)
                ? sb.Remove(position, newString.Length).Insert(position, newString)
                : sb.Remove(position, length).Insert(position, newString.Substring(0, length));
    }

Используйте это так:

var theString = new string(' ', 10);
var sb = new StringBuilder(theString);
sb.Replace(5, "foo");
return sb.ToString();

-2

Я считаю, что самый простой способ был бы так: (без stringbuilder)

string myString = "ABCDEFGHIJ";
char[] replacementChars = {'Z', 'X'};
byte j = 0;

for (byte i = 3; i <= 4; i++, j++)  
{                   
myString = myString.Replace(myString[i], replacementChars[j]);  
}

Это работает, потому что переменная типа string может рассматриваться как массив переменных типа char. Например, вы можете ссылаться на второй символ строковой переменной с именем «myString» как myString [1]


Это работает только потому, что в примере автора stringзаменяемые символы встречаются только один раз. Если вы столкнетесь с этим, скажем, "DBCDEFGHIE"тогда вы получите "ZBCZXFGHIX", что не является желаемым результатом. Кроме того, я не согласен, что это проще, чем другие не- StringBuilderрешения, которые были опубликованы два с половиной года назад.
— Бекон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.