Best way to remove the last character from a string built with stringbuilder

I have the following

data.AppendFormat("{0},",dataToAppend);

The problem with this is that I am using it in a loop and there will be a trailing comma. What is the best way to remove the trailing comma?

Do I have to change data to a string and then substring it?

123947 次浏览

在循环后使用以下内容。

.TrimEnd(',')

或者干脆变成

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)

是的,循环结束后将其转换为字符串:

String str = data.ToString().TrimEnd(',');

我建议您更改循环算法:

  • 添加逗号不是在项目之后,而是之前
  • 使用以 false 开头的布尔变量,禁止显示第一个逗号
  • 测试之后,将这个布尔变量设置为 true

Just use

string.Join(",", yourCollection)

这样你就不需要 StringBuilder和循环了。




关于异步情况的长期补充。 截至2019年,异步数据到来的情况并不少见。

如果您的数据处于异步收集中,则不会有占用 IAsyncEnumerable<T>string.Join重载。但是手动创建一个很容易,黑进 string.Join的密码:

public static class StringEx
{
public static async Task<string> JoinAsync<T>(string separator, IAsyncEnumerable<T> seq)
{
if (seq == null)
throw new ArgumentNullException(nameof(seq));


await using (var en = seq.GetAsyncEnumerator())
{
if (!await en.MoveNextAsync())
return string.Empty;


string firstString = en.Current?.ToString();


if (!await en.MoveNextAsync())
return firstString ?? string.Empty;


// Null separator and values are handled by the StringBuilder
var sb = new StringBuilder(256);
sb.Append(firstString);


do
{
var currentValue = en.Current;
sb.Append(separator);
if (currentValue != null)
sb.Append(currentValue);
}
while (await en.MoveNextAsync());
return sb.ToString();
}
}
}

如果数据是异步传递的,但是不支持接口 IAsyncEnumerable<T>(如注释 SqlDataReader中提到的) ,那么将数据封装到 IAsyncEnumerable<T>中相对容易:

async IAsyncEnumerable<(object first, object second, object product)> ExtractData(
SqlDataReader reader)
{
while (await reader.ReadAsync())
yield return (reader[0], reader[1], reader[2]);
}

并使用它:

Task<string> Stringify(SqlDataReader reader) =>
StringEx.JoinAsync(
", ",
ExtractData(reader).Select(x => $"{x.first} * {x.second} = {x.product}"));

为了使用 Select,您需要使用 nuget 包 System.Interactive.Async.给你,您可以找到一个可编译的示例。

应该使用 string.Join方法将项的集合转换为逗号分隔的字符串。它将确保没有前导逗号或尾随逗号,并确保有效构造字符串(没有不必要的中间字符串)。

你有两个选择。第一种是很容易使用的 Remove方法,它是相当有效的。第二种方法是使用带有开始索引和结束索引(MSDN 文档)的 ToString

Similar SO question here.

我喜欢 using 一个 StringBuilder 扩展方法。

RemoveLast Method

最简单的 最有效的方法就是执行以下命令:

data.Length--;

通过这样做,你可以将指针(例如最后一个索引)移回一个字符,但是不会改变对象的可变性。事实上,清除 StringBuilder最好也使用 Length(但为了清楚起见,实际上使用了 Clear()方法,因为它的实现看起来就是这样) :

data.Length = 0;

因为它不会改变分配表。你可以这样想,我不想再认识这些字节了。现在,即使调用 ToString(),它也不能识别除了它的 Length之外的任何东西,嗯,它不能。这是一个可变对象,它分配的空间比你提供的要多,它只是这样构建的。

最简单的方法是使用 Join ()方法:

public static void Trail()
{
var list = new List<string> { "lala", "lulu", "lele" };
var data = string.Join(",", list);
}

如果你真的需要 StringBuilder,修剪循环后面的逗号:

data.ToString().TrimEnd(',');

这样吧。

string str = "The quick brown fox jumps over the lazy dog,";
StringBuilder sb = new StringBuilder(str);
sb.Remove(str.Length - 1, 1);

我更喜欢控制字符串生成器的长度:

data.Length = data.Length - 1;

Gotcha!!

如果你像下面这样使用 AppendLine,这个帖子中的大多数答案都不会起作用:

var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length--; // Won't work
Console.Write(builder.ToString());


builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length += -1; // Won't work
Console.Write(builder.ToString());


builder = new StringBuilder();
builder.AppendLine("One,");
Console.Write(builder.TrimEnd(',')); // Won't work

玩弄我

为什么!

这个问题很简单,但我花了一段时间才弄明白: 因为在 CRLF(回车和换行)的末尾还有2个 隐形的字符。因此,你需要去掉最后3个字符:

var builder = new StringBuilder();
builder.AppendLine("One,");
builder.Length -= 3; // This will work
Console.WriteLine(builder.ToString());

结语

如果上次调用的方法是 Append,则使用 Length--Length -= 1。如果上一次调用 AppendLine的方法是 Length =- 3,则使用 Length =- 3