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

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");
V4Vendetta
источник
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();
    }
}
Али Халид
источник
5
        string s = "ABCDEFG";
        string t = "st";
        s = s.Remove(4, t.Length);
        s = s.Insert(4, t);
Джавед Акрам
источник
Это работает, вы, вероятно, не хотите tвозвращать снова.
Пьер
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));
        });
}

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

svick
источник
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"разной длины.
Джон Алексиу
3

Еще один

    public static string ReplaceAtPosition(this string self, int position, string newValue)        
    {
        return self.Remove(position, newValue.Length).Insert(position, newValue); 
    }
Стейн Ван Антверпен
источник
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");
    }
}
Hassan
источник
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);
user1355816
источник
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();
MovGP0
источник
-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]

Sfou
источник
Это работает только потому, что в примере автора stringзаменяемые символы встречаются только один раз. Если вы столкнетесь с этим, скажем, "DBCDEFGHIE"тогда вы получите "ZBCZXFGHIX", что не является желаемым результатом. Кроме того, я не согласен, что это проще, чем другие не- StringBuilderрешения, которые были опубликованы два с половиной года назад.
Бекон