如何高效地从 StringBuilder 的末尾删除空格,而不使用 ToString().Trim() 方法并将其转换为新的 StringBuilder(new StringBuilder(sb.ToString().Trim()))?
sb.TrimEnd();
sb.TrimEnd().AppendLine()
)。public static StringBuilder TrimEnd(this StringBuilder sb)
{
if (sb == null || sb.Length == 0) return sb;
int i = sb.Length - 1;
for (; i >= 0; i--)
if (!char.IsWhiteSpace(sb[i]))
break;
if (i < sb.Length - 1)
sb.Length = i + 1;
return sb;
}
char.IsWhiteSpace
。因此,当不需要时,调用TrimEnd
几乎没有任何费用,而不是这些ToString().Trim()
返回SB路线。char.IsWhiteSpace
(在第一个非空格字符上中断)。当然,循环向后迭代;如果全部都是空格,您将以SB.Length
为0结束。i
索引在循环外,这使我们可以相应地裁剪长度。在StringBuilder
中,这非常有效,它只是设置一个内部长度整数(内部char[]
保持相同的内部长度)。另外,请参见这篇很好的文章,讨论.NET 4.0的新
StringBuilder
在技术上是char[]
块的链接列表,因此我们不会进入LOH。调整长度并不像简单地更改结束索引那样简单,因为如果移动到不同的块中,则必须维护Capacity,因此可能需要分配新块。尽管如此,您只在最后设置Length属性,因此这似乎是一个很好的解决方案。 来自Eric Lippert的相关详细信息:https://dev59.com/KWw15IYBdhLWcg3wkcuq#6524401
StringBuilder
实现:http://1024strongoxen.blogspot.com/2010/02/net-40-stringbuilder-implementation.html
更新:以下说明了更改StringBuilder
长度时会发生什么(这里唯一真正执行的操作,而且仅在需要时才执行):StringBuilder sb = new StringBuilder("cool \t \r\n ");
sb.Capacity.Print(); // 16
sb.Length.Print(); // 11
sb.TrimEnd();
sb.Capacity.Print(); // 16
sb.Length.Print(); // 4
在更改长度后,您可以看到内部数组(m_ChunkChars
)保持相同的大小,实际上,在调试器中可以看到它甚至不会覆盖(在这种情况下是空格)字符。它们只是被遗弃了。
StringBuilder
在技术上是一个 char[]
块的链表,因此我们不会陷入 LOH。调整长度并不像简单地更改结束索引那样“技术性”,因为如果您移动到另一个块中,则必须维护 Capacity
,因此可能需要分配新块。尽管如此,您只在最后设置 Length
属性,因此这似乎是一个很好的解决方案。 - Ryan EmerleStringBuilder b = new StringBuilder();
b.Append("some words");
b.Append(" to test ");
int count = 0;
for (int i = b.Length - 1; i >= 0; i--)
{
if (b[i] == ' ')
count++;
else
break;
}
b.Remove(b.Length - count, count);
string result = b.ToString();
它将只是迭代到末尾,同时有空格然后跳出循环。
或者像这样:
StringBuilder b = new StringBuilder();
b.Append("some words");
b.Append(" to test ");
do
{
if(char.IsWhiteSpace(b[b.Length - 1]))
{
b.Remove(b.Length - 1,1);
}
}
while(char.IsWhiteSpace(b[b.Length - 1]));
string get = b.ToString();
我为Nicholas Petersen的版本进行了扩展,以包含可选的附加字符:
/// <summary>
/// Trims the end of the StingBuilder Content. On Default only the white space char is truncated.
/// </summary>
/// <param name="pTrimChars">Array of additional chars to be truncated.</param>
/// <returns></returns>
public static StringBuilder TrimEnd(this StringBuilder pStringBuilder, char[] pTrimChars = null)
{
if (pStringBuilder == null || pStringBuilder.Length == 0)
return pStringBuilder;
int i = pStringBuilder.Length - 1;
var lTrimChars = new HashSet<char>();
if (pTrimChars != null)
lTrimChars = pTrimChars.ToHashSet();
for (; i >= 0; i--)
{
var lChar = pStringBuilder[i];
if ((char.IsWhiteSpace(lChar) == false) && (lTrimChars.Contains(lChar) == false))
break;
}
if (i < pStringBuilder.Length - 1)
pStringBuilder.Length = i + 1;
return pStringBuilder;
}
编辑:在Nicholas Petersen的建议下:
/// <summary>
/// Trims the end of the StingBuilder Content. On Default only the white space char is truncated.
/// </summary>
/// <param name="pTrimChars">Array of additional chars to be truncated. A little bit more efficient than using char[]</param>
/// <returns></returns>
public static StringBuilder TrimEnd(this StringBuilder pStringBuilder, HashSet<char> pTrimChars = null)
{
if (pStringBuilder == null || pStringBuilder.Length == 0)
return pStringBuilder;
int i = pStringBuilder.Length - 1;
for (; i >= 0; i--)
{
var lChar = pStringBuilder[i];
if (pTrimChars == null)
{
if (char.IsWhiteSpace(lChar) == false)
break;
}
else if ((char.IsWhiteSpace(lChar) == false) && (pTrimChars.Contains(lChar) == false))
break;
}
if (i < pStringBuilder.Length - 1)
pStringBuilder.Length = i + 1;
return pStringBuilder;
}
public static class StringBuilderExtensions
{
public static StringBuilder Trim(this StringBuilder builder)
{
if (builder.Length == 0)
return builder;
var count = 0;
for (var i = 0; i < builder.Length; i++)
{
if (!char.IsWhiteSpace(builder[i]))
break;
count++;
}
if (count > 0)
{
builder.Remove(0, count);
count = 0;
}
for (var i = builder.Length - 1; i >= 0; i--)
{
if (!char.IsWhiteSpace(builder[i]))
break;
count++;
}
if (count > 0)
builder.Remove(builder.Length - count, count);
return builder;
}
}
TrimToString
,如果需要修剪开头,你可以使用ToString重载来设置开始索引以从中获取字符串(并先用正常方式修剪结尾)。我已经使用这个方法一段时间了,稍后会发布新的帖子介绍它。 - Nicholas PetersenStringBuilder
级别上执行,而是在ToString
时执行,例如使用此TrimToString
实现: public static string TrimToString(this StringBuilder sb)
{
if (sb == null) return null;
sb.TrimEnd(); // handles nulle and is very inexpensive, unlike trimstart
if (sb.Length > 0 && char.IsWhiteSpace(sb[0])) {
for (int i = 0; i < sb.Length; i++)
if (!char.IsWhiteSpace(sb[i]))
return sb.ToString(i);
return ""; // shouldn't reach here, bec TrimEnd should have caught full whitespace strings, but ...
}
return sb.ToString();
}
ToString
中添加第二个参数:return sb.ToString(i, sb.Length - i)
。 - Сергей РыбаковToString
方法,只有两个参数的。我在第一条评论中的示例允许在 .net core 2.1 上使用您优秀的 TrimToString
方法。 - Сергей РыбаковStringBuilder myString = new StringBuilder("This is Trim test ");
if (myString[myString.Length - 1].ToString() == " ")
{
myString = myString.Remove(myString.Length - 1, 1);
}
StringBuilder
来实现你的方法,那么你的方法会很快。但如果你想在需要修剪之前就放弃它并使用结果字符串,那么对该字符串应用TrimEnd()
方法会更快。我很想看到一个情况,证明你的代码比其他任何实现方法都快上数千倍。 - Ryan Emerle