简短版
如果您的目标是获得StringBuilder
内容的一部分或全部,并将其放入String
对象中,您应该使用其ToString
函数。但如果您还没有完成创建字符串,则最好将StringBuilder
视为字符数组并以此方式操作,而不是创建大量您不需要的字符串。
对字符数组进行字符串操作可能会因本地化或编码而变得复杂,因为字符串可以以许多方式进行编码(例如UTF8或Unicode),但其字符(System.Char
)应为16位UTF16值。
我编写了以下方法,如果字符串存在于StringBuilder
中,则返回其索引;否则返回-1。 您可以使用此方法创建其他常见的String
方法,例如Contains
,StartsWith
和EndsWith
。 由于此方法应正确处理本地化和大小写,而且不强制您在StringBuilder
上调用ToString
,因此它比其他方法更可取。 如果您指定应忽略大小写,则它将创建一个垃圾值,并且可以通过使用Char.ToLower来最大化内存节省来修复此问题,而不是像在下面的函数中那样预先计算字符串的小写形式。编辑:此外,如果您使用UTF32编码的字符串,则必须一次比较两个字符而不仅仅是一个。
除非您要循环操作、处理大型字符串并进行操作或格式化,否则最好使用ToString
。
public static int IndexOf(this StringBuilder stringBuilder, string str, int startIndex = 0, int? count = null, CultureInfo culture = null, bool ignoreCase = false)
{
if (stringBuilder == null)
throw new ArgumentNullException("stringBuilder");
if (str == null)
throw new ArgumentNullException("str");
if (str.Length == 0)
return -1;
if (startIndex < 0 && startIndex < stringBuilder.Length)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "The index must refer to a character within the string.");
var maxPositions = stringBuilder.Length - str.Length - startIndex;
if (maxPositions <= 0) return -1;
if (count.HasValue && (count <= 0 || count > maxPositions))
throw new ArgumentOutOfRangeException("count");
maxPositions = count ?? maxPositions;
if (count <= 0) return -1;
culture = culture ?? CultureInfo.CurrentCulture;
if (ignoreCase) str = str.ToLower(culture);
for (int y = 0, x = startIndex, endIndex = startIndex + maxPositions; x <= endIndex; x++, y = 0)
{
while (y < str.Length && str[y] == (ignoreCase ? Char.ToLower(str[x + y]) : str[x + y]))
y++;
if (y == str.Length)
return x;
}
return -1;
}
通常情况下,我们使用
StringBuilder
对象而非字符串拼接的主要原因是字符串不可变性所带来的内存开销。如果你在进行大量字符串操作时没有使用
StringBuilder
,那么看到的性能损失往往是由于一路上创建了许多垃圾字符串。举个例子:
string firstString = "1st",
secondString = "2nd",
thirdString = "3rd",
fourthString = "4th";
string all = firstString;
all += " & " + secondString;
all += " &" + thirdString;
all += "& " + fourthString + ".";
如果你运行这段代码并在内存分析器中打开它,你会发现一组字符串,看起来像这样:
"1st", "2nd", "3rd", "4th",
" & ", " & 2nd", "1st & 2nd"
" &", "&3rd", "1st & 2nd &3rd"
"& ", "& 4th", "& 4th."
"1st & 2nd &3rd& 4th."
在那个作用域中我们创建了14个对象,但如果你没有意识到每个加法运算符都会创建一个全新的字符串,你可能会认为只有5个。那么其他九个字符串会发生什么呢?它们在内存中闲置,直到垃圾回收器决定回收它们。
所以现在我的观点就是:如果你想要查找关于StringBuilder对象的信息,而你不想调用ToString(),那么很可能意味着你还没有完成构建字符串的工作。如果你想知道构建器是否以"Foo"结尾,调用
sb.ToString(sb.Length - 1, 3) == "Foo"
是很浪费的,因为你正在创建另一个字符串对象,一旦你调用完毕,它就变得无用了。
我猜你正在运行一个循环将文本聚合到你的StringBuilder中,并且如果最后几个字符是你期望的标志值,你想结束循环或者做一些不同的事情。
StartsWith("world")
? - Arian MotamediToString
?这就是你使用 StringBuilders 的方式。 - banging