У меня есть эта строка: ABCDEFGHIJ
Мне нужно заменить из положения 4 в положение 5 строкой ZX
Это будет выглядеть так: ABCZXFGHIJ
Но не использовать с string.replace("DE","ZX")- мне нужно использовать с позиции
Как мне это сделать?
У меня есть эта строка: ABCDEFGHIJ
Мне нужно заменить из положения 4 в положение 5 строкой ZX
Это будет выглядеть так: ABCZXFGHIJ
Но не использовать с string.replace("DE","ZX")- мне нужно использовать с позиции
Как мне это сделать?
Ответы:
Самый простой способ добавить и удалить диапазоны в строке - использовать StringBuilder.
var theString = "ABCDEFGHIJ";
var aStringBuilder = new StringBuilder(theString);
aStringBuilder.Remove(3, 2);
aStringBuilder.Insert(3, "ZX");
theString = aStringBuilder.ToString();
Альтернативой является использование String.Substring, но я думаю, что StringBuilderкод становится более читабельным.
string s = "ABCDEFGH";
s= s.Remove(3, 2).Insert(3, "ZX");
Вот метод расширения, который не использует 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 не нужно инициализировать и поскольку он использует более простые операции. Пожалуйста, поправьте меня, если я ошибаюсь. :)
Используйте String.Substring()(подробности здесь ), чтобы вырезать левую часть, затем замену, затем правую часть. Играйте с индексами, пока не поймете как надо :)
Что-то вроде:
string replacement=original.Substring(0,start)+
rep+original.Substring(start+rep.Length);
string s = "ABCDEFG";
string t = "st";
s = s.Remove(4, t.Length);
s = s.Insert(4, t);
tвозвращать снова.
Если вы заботитесь о производительности, то здесь вам нужно избегать распределения. И если вы на .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));
});
}
Этот подход сложнее для понимания, чем альтернативы, но он единственный, который выделяет только один объект на вызов: вновь созданная строка.
Как и другие упомянули, 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. Так что это немного медленнее.
"ABCDEFGHIJ"и "ZX"разной длины.
Еще один
public static string ReplaceAtPosition(this string self, int position, string newValue)
{
return self.Remove(position, newValue.Length).Insert(position, newValue);
}
С помощью этого поста я создаю следующую функцию с дополнительными проверками длины
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");
}
}
Все остальные ответы не работают, если строка содержит символ 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) : ""
));
}
}
Я искал решение со следующими требованиями:
Решение, которое лучше всего подходит мне, это:
// replace `oldString[i]` with `c`
string newString = new StringBuilder(oldString).Replace(oldString[i], c, i, 1).ToString();
Это использует StringBuilder.Replace(oldChar, newChar, position, count)
Другое решение, которое удовлетворяет моим требованиям, заключается в использовании Substringс конкатенацией:
string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);
Это тоже нормально. Я думаю, что это не так эффективно, как в первом случае с производительностью (из-за ненужной конкатенации строк). Но преждевременная оптимизация - корень всего зла .
Так что выбирайте тот, который вам нравится больше всего :)
Лучше использовать String.substr().
Как это:
ReplString = GivenStr.substr(0, PostostarRelStr)
+ GivenStr(PostostarRelStr, ReplString.lenght());
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; }
И последнее, но не менее важное: это решение лучше всего подходит для этой конкретной проблемы, когда требуется заменить только несколько символов с заранее известным индексом позиции. Это может быть не лучшим вариантом, когда требуется изменить довольно длинную строку, т. Е. Количество символов для изменения велико.
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);
Вот простой метод расширения:
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();
Я считаю, что самый простой способ был бы так: (без 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решения, которые были опубликованы два с половиной года назад.