StringWriter或StringBuilder

83

StringWriterStringBuilder有什么区别?我应该何时使用它们中的哪一个?

7个回答

103
我认为现有的答案都没有真正回答这个问题。两个类之间的实际关系是适配器模式的一个例子。
StringWriter通过将所有的Write...方法转发到它存储在字段中的StringBuilder实例来实现。这不仅仅是一个内部细节,因为StringWriter有一个公共方法GetStringBuilder,它返回内部字符串构建器,并且一个构造函数允许你传入一个现有的StringBuilder。
所以StringWriter是一个适配器,它允许StringBuilder作为一个目标被那些期望使用TextWriter的代码使用。在基本行为方面,它们之间显然没有什么可选择的...除非你可以测量转发调用的开销,在这种情况下,StringWriter略慢一些,但这似乎不太可能是显著的。

那么为什么他们没有直接让StringBuilder实现TextWriter呢?这是一个灰色地带,因为接口背后的意图并不总是一眼就能看清。

TextWriter几乎是接受字符流的接口。但它有一个额外的复杂之处:一个名为Encoding的属性。这意味着TextWriter是一个接受字符流的接口并将其转换为字节的接口。

StringWriter中,这是一个无用的遗物,因为它不执行编码。documentation表示:

对于某些 XML 场景,需要写入一个包含 StringWriter 使用的编码的标头。这允许 XML 代码使用任意 StringWriter 并生成正确的 XML 标头。

但这不对,因为我们无法指定StringWriterEncoding值。该属性始终具有UnicodeEncoding的值。因此,任何检查此属性以构建XML头部的代码都会始终返回utf-16。例如:
var stringWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringWriter))
    xDocument.WriteTo(xmlWriter);

这会生成标题:

<?xml version="1.0" encoding="utf-16"?>

如果您使用File.WriteAllText将XML字符串写入文件,会怎样呢?默认情况下,您将拥有一个带有utf-16头的utf-8文件。
在这种情况下,更安全的做法是使用StreamWriter,并使用文件路径或FileStream进行构造,或者如果您想检查数据,则使用MemoryStream获取字节数组。所有这些组合都可以确保编码为字节和标头生成由您的StreamWriter中的相同Encoding值引导。 Encoding属性的目的是允许字符流的生成器将关于编码的准确信息包含到字符流本身中(例如在XML示例中;其他示例包括电子邮件标题等)。
但是通过引入StringWriter,内容生成和编码之间的链接被断开,因此这种自动机制停止工作并变得潜在错误。

然而,StringWriter是一个有用的适配器,但您需要小心使用,即您应该理解您的内容生成代码不应依赖于Encoding属性的无意义值。但这种警告通常与适配器模式相关联。这通常是一种hack,允许您将类似方形但几乎是圆形的钉子放入圆洞中。


@SandRock请看第二段。 - Daniel Earwicker
你可以创建一个派生的 StringWriter 类型,它使用不同的编码方式 - ejohnson
很少有问题是@Jon Skeet不是最高得分者的。 - justAbit

61

StringWriter继承自TextWriter,允许各种类在不考虑数据流入处的情况下写文本。对于StringWriter来说,输出只是进入内存中。如果您调用需要TextWriter的API,但只想在内存中构建结果,则可以使用此功能。

StringBuilder实质上是一个缓冲区,允许您对“逻辑字符串”执行多个操作(通常是追加),而无需每次创建新的字符串对象。您可以使用此功能来通过多个操作构建字符串。


10

在之前的(好)回答的基础上,StringWriter 实际上比 StringBuilder 更加多用途,提供了许多重载函数。

例如:

虽然 StringBuilder 仅接受字符串或无内容的参数作为 AppendLine 方法的输入。

StringBuilder sb = new StringBuilder();
sb.AppendLine("A string");

StringWriter可以直接接受字符串格式。

StringWriter sw = new StringWriter();
sw.WriteLine("A formatted string {0}", DateTime.Now);

使用 StringBuilder,必须这样做(或使用string.Format$""

sb.AppendFormat("A formatted string {0}", DateTime.Now);
sb.AppendLine();

虽然不是生死攸关的事情,但仍然有所不同。


3

StringBuilder 类基本上是一个可变字符串,它是一个帮助类,用于构建不可变字符串。在此基础上,StringWriter 提供了更多方便的字符串格式化函数。


2

0

StringBuilder 用于大量字符串拼接。据我所知,对于超过5个字符串,它比 String.Concat() 更有效。此外,它还可以与特定格式一起使用(.AppendFormat())。


你最后一段所说的适用于StringBuilder,而不是StringWriter。 - Konrad Rudolph
1
哦,MSDN说“StringWriter类实现了一个TextWriter,用于将信息写入字符串”http://msdn.microsoft.com/en-us/library/system.io.stringwriter.aspx - abatishchev

-2

StringBuilderStringReader 在不同的情况下用于提高性能。
使用 StringBuilder 来提高字符串操作的性能,例如连接字符串、重复修改字符串。

Random rnd = new Random();
StringBuilder sb = new StringBuilder();

// Generate 10 random numbers and store in sb.
for (int i = 0; i < 10; i++)
{
    sb.Append(rnd.Next().ToString("N5"));
}
Console.WriteLine("The original string:");
Console.WriteLine(sb.ToString());

// Decrease each number by one.
for (int ctr = 0; ctr < sb.Length; ctr++)
{
    if (Char.GetUnicodeCategory(sb[ctr]) == System.Globalization.UnicodeCategory.DecimalDigitNumber)
    {
        int number = (int)Char.GetNumericValue(sb[ctr]);
        number--;
        if (number < 0)
            number = 9;

        sb[ctr] = number.ToString()[0];
    }
}
Console.WriteLine("\nThe new string:");
Console.WriteLine(sb.ToString());

使用StringReader来解析大量文本的分行,并在处理数据时最小化内存使用。请参见下一个示例,其中StringReader上的ReadLine方法只是从当前位置开始扫描下一个换行符,然后基于字段字符串返回一个子字符串。
using (StringReader sr = new StringReader("input.txt"))
{
    // Loop over the lines in the string or txt file.
    int count = 0;
    string line;
    while((line = sr.ReadLine()) != null)
    {
        count++;
        Console.WriteLine("Line {0}: {1}", count, line);
    }
}

1
我不明白为什么有人会点赞这个答案;OP的问题是关于StringWriter而不是StringReader - Zev Spitz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接