I believe the simplest way would be this:(without 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]);
}
This works because a variable of type string can be treated as an array of char variables.
You can, for example refer to the second character of a string variable with name "myString" as myString[1]
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();
}
}
Here's an extension method that doesn't use StringBuilder or Substring. This method also allows the replacement string to extend past the length of the source string.
//// 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);
}
When using this function, if you want the entire replacement string to replace as many characters as possible, then set length to the length of the replacement string:
I guess this is more efficient since the StringBuilder class need not be initialized and since it uses more basic operations. Please correct me if I am wrong. :)
All others answers don't work if the string contains Unicode char (like Emojis) because an Unicode char weight more bytes than a char.
Example : the emoji '🎶' converted to bytes, will weight the equivalent of 2 chars. So, if the unicode char is placed at the beginning of your string, offset parameter will be shifted).
With this topic, i extend the StringInfo class to Replace by position keeping the Nick Miller's algorithm to avoid that :
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) : ""
));
}
}
The other solution that satisfies my requirements is to use Substring with concatenation:
string newString = oldStr.Substring(0, i) + c + oldString.Substring(i+1, oldString.Length);
This is OK too. I guess it's not as efficient as the first one performance wise (due to unnecessary string concatenation). But premature optimization is the root of all evil.
If you care about performance, then the thing you want to avoid here are allocations. And if you're on .Net Core 2.1+, then you can, by using the string.Create method:
This approach is harder to understand than the alternatives, but it's the only one that will allocate only one object per call: the newly created string.
string myString = "ABCDEFGHIJ";
string modifiedString = new StringBuilder(myString){[3]='Z', [4]='X'}.ToString();
Let me explain my solution.
Given the problem statement of altering a string in its two specific position (“position 4 to position 5”) with two character ‘Z’ and ‘X’ and the ask is to use the position index to alter the string and not string Replace() method(may be because of the possibility of repetition of some characters in the actual string), I would prefer to use minimalist approach to achieve the goal over using Substring() and string Concat() or string Remove() and Insert() approach. Though all those solutions will serve the purpose in attaining the same goal, but it just depends on personal choice and philosophy of settling with minimalist approach.
Coming back to my solution mention above, if we take a closer look of string and StringBuilder, both of them internally treats a given string as an array of characters. If we look at the implementation of StringBuilder it maintains an internal variable something like “internal char[] m_ChunkChars;” to capture the given string. Now since this is an internal variable, we cannot directly access the same. For external world, to be able to access and alter that character array, StringBuilder exposes them through indexer property which looks something like below
[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
}
}
}
My solution mentioned above leverage this indexer capability to directly alter the internally maintained character array which IMO is efficient and minimalist.
BTW; we can rewrite the above solution more elaborately something like below
In this context also would like to mention that in case of string it also have indexer property as a means to expose its internal character array, but in this case it only has Getter property (and no Setter) as string is immutable in nature. And that is why we need to use StringBuilder to alter the character array.
And last but not the least this solution is only best fit for this specific problem where the ask is to replace only few characters with a known position index upfront. It may not be the best fit when the requirement is to alter a fairly lengthy string i.e. number of characters to alter are large in numbers.
Dim QTT As Double
If IsDBNull(dr.Item(7)) Then
QTT = 0
Else
Dim value As String = dr.Item(7).ToString()
Dim posicpoint As Integer = value.LastIndexOf(".")
If posicpoint > 0 Then
Dim v As New Text.StringBuilder(value)
v.Remove(posicpoint, 1)
v.Insert(posicpoint, ",")
QTT = Convert.ToDouble(v.ToString())
Else
QTT = Convert.ToDouble(dr.Item(7).ToString())
End If
Console.WriteLine(QTT.ToString())
End If