У меня есть эта строка: 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 или подстроку. Этот метод также позволяет заменяющей строке проходить за длину исходной строки.
//// 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);
Как метод расширения.
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();
}
}
string s = "ABCDEFG";
string t = "st";
s = s.Remove(4, t.Length);
s = s.Insert(4, t);
Вы можете попробовать что-то связать это:
string str = "ABCDEFGHIJ";
str = str.Substring(0, 2) + "ZX" + str.Substring(5);
Как и другие, упомянутая функция 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. Так что он немного медленнее.
Все остальные ответы не работают, если строка содержит символ Unicode (например, Emojis), потому что символ Unicode весит больше байтов, чем символ.
Пример: смайлики '🎶', преобразованные в байты, будут весить эквивалент 2 символов. Таким образом, если символ unicode находится в начале вашей строки, параметр
offset
будет смещен).
В этой теме я расширил класс StringInfo до Replace by position, сохраняя алгоритм Ника Миллера, чтобы избежать этого:
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) : ""
));
}
}
С помощью этого сообщения я создаю следующую функцию с дополнительными проверками длины
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");
}
}
Еще один
public static string ReplaceAtPosition(this string self, int position, string newValue)
{
return self.Remove(position, newValue.Length).Insert(position, newValue);
}
Если вы заботитесь о производительности, то здесь вам следует избегать распределения. А если вы используете .Net Core 2. 1+ (или, пока не выпущенный,.Net Standard 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));
});
}
Этот подход сложнее для понимания, чем альтернативы, но он единственный, который выделяет только один объект на вызов: вновь созданная строка.
Я искал решение со следующими требованиями:
Решение, которое лучше всего подходит мне, это:
// 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()
и string Concat()
или string 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);
Я считаю, что самым простым способом было бы это: (без 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]