I understand that I can call ToString().IndexOf(...), but I don't want to create an extra string. I understand that I can write a search routine manually. I just wonder why such a routine doesn't already exist in the framework.
Asked
Active
Viewed 2.1k times
4 Answers
58
I know this is an old question, however I have written a extension method that performs an IndexOf
on a StringBuilder
. It is below. I hope it helps anyone that finds this question, either from a Google search or searching StackOverflow.
/// <summary>
/// Returns the index of the start of the contents in a StringBuilder
/// </summary>
/// <param name="value">The string to find</param>
/// <param name="startIndex">The starting index.</param>
/// <param name="ignoreCase">if set to <c>true</c> it will ignore case</param>
/// <returns></returns>
public static int IndexOf(this StringBuilder sb, string value, int startIndex, bool ignoreCase)
{
int index;
int length = value.Length;
int maxSearchLength = (sb.Length - length) + 1;
if (ignoreCase)
{
for (int i = startIndex; i < maxSearchLength; ++i)
{
if (Char.ToLower(sb[i]) == Char.ToLower(value[0]))
{
index = 1;
while ((index < length) && (Char.ToLower(sb[i + index]) == Char.ToLower(value[index])))
++index;
if (index == length)
return i;
}
}
return -1;
}
for (int i = startIndex; i < maxSearchLength; ++i)
{
if (sb[i] == value[0])
{
index = 1;
while ((index < length) && (sb[i + index] == value[index]))
++index;
if (index == length)
return i;
}
}
return -1;
}

Dennis
- 20,275
- 4
- 64
- 80
-
1It's said [Use the String.ToUpperInvariant method instead of the String.ToLowerInvariant method when you normalize strings for comparison](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#recommendations-for-string-usage). I guess this is also true for characters comparison: [Char.ToUpper and Char.ToLower work similarly to the String.ToUpper and String.ToLower methods described in the previous section](https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#chartoupper-and-chartolower) – Serg Feb 24 '18 at 14:28
-
Fair call; feel free to make the edits to the above post. – Dennis Feb 26 '18 at 02:11
-
5Sorry, I was wrong. Actually Microsoft has optimized *ToUpperInvariant()*, not *ToUpper()*. In this case, we can't provide *ToUpperInvariant()* by default as far as it can lead to the wrong comparison in some languages like Turkish. So, in this regard, your code is absolutely correct. – Serg Feb 26 '18 at 10:55
-
3It's ok to be wrong. Thanks for coming back and commenting again. :+1: – Dennis Feb 27 '18 at 04:35
-
@Sergey Isn't it the other way around: `ToUpperInvariant` *should* be used because it correctly upper-cases the Turkish-I whereas `ToLower` and `ToLowerInvariant` is incorrect? – Dai Mar 03 '20 at 08:24
-
@Dai In my second comment I said that `ToUpperInvariant()` is optimized but cannot be used for Turkish. Only `ToLower()` & `ToUpper()` will provide the correct result. >>> https://dotnetfiddle.net/YNO52f <<< So, I can't agree that *ToUpperInvariant should be used because it correctly upper-cases the Turkish-I*. – Serg Mar 03 '20 at 21:04
-
Edited `ToLower()` -> to `ToUpper()` for performance reasons mentioned above (pending approval). I would have chosen `ToUpperInvariant()`, but some above said it didn't work for them as expected (eg, for Turkish). – dylanh724 Aug 16 '21 at 07:21
8
Unfortunately, many of the methods implemented for String could have been implemented for StringBuilder but that was not done. Consider using extension methods to add what you care about.

Jason Kresowaty
- 16,105
- 9
- 57
- 84
3
Dennis, great solution. Thanks. I suggest to optimize it a little:
public static int IndexOf(
this StringBuilder sb,
string value,
int startIndex,
bool ignoreCase)
{
int len = value.Length;
int max = (sb.Length - len) + 1;
var v1 = (ignoreCase)
? value.ToLower() : value;
var func1 = (ignoreCase)
? new Func<char, char, bool>((x, y) => char.ToLower(x) == y)
: new Func<char, char, bool>((x, y) => x == y);
for (int i1 = startIndex; i1 < max; ++i1)
if (func1(sb[i1], v1[0]))
{
int i2 = 1;
while ((i2 < len) && func1(sb[i1 + i2], v1[i2]))
++i2;
if (i2 == len)
return i1;
}
return -1;
}

Roman Koff
- 83
- 1
- 8
-2
Calling ToString()
on a StringBuilder
doesn't create an extra object, confusingly. Internally, StringBuilder
stores a String object, for performance; calling ToString()
simply returns that object.

Michael Petrotta
- 59,888
- 27
- 145
- 179
-
this is not correct in the context of the question if the stringbuilder is asked to modify itself Tthen a *new* string is created, the mutability of the internal buffer is not exposed to managed code. – ShuggyCoUk Sep 01 '09 at 00:04
-
-
12To clarify the previous comment, there is not much overhead from calling ToString. But after you call it, the next modification to the StringBuilder will incur copying overhead. (This is a valid optimzation because ToString is usually the last thing done to a StringBuilder.) As a result of this, efficient implementations of String-like methods can't use ToString, which precludes a trivial solution to the original poster's issue. – Jason Kresowaty Sep 01 '09 at 02:10
-
@binarycoder: Is that optimization still performed? I thought `StringBuilder.ToString` was changed to produce a new `String` object unless it can reuse a string that has already been returned, to avoid the possibility of one thread calling `ToString` while another thread is mutating the `StringBuilder`. While there's no spec as to what string should be returned in that case, there shouldn't be any way by which the string which is returned can be modified after the return; the only way I know to ensure that is to return a string which has never and will never be open to modification. – supercat Nov 20 '12 at 22:12
-
7This answer is misleading as it suggests that the `ToString` method simply returns an already available string. That is not the case. There is some weird code using `wstrcpy`(looked at the decompiled code in ILSpy, .NET4) to create a new string from this stringbuilder. – Tim Schmelter May 13 '14 at 09:05
-
Hmm. I'll peek at the source when I'm next near a computer. Thanks, @Tim. – Michael Petrotta May 13 '14 at 15:39
-
4Here is source: http://referencesource.microsoft.com/#mscorlib/system/text/stringbuilder.cs,5a97da49a158a3c9 That is clearly not a no-op. – Tim Schmelter Sep 20 '16 at 08:26