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