61

将 StringBuilder 写入 System.IO.Stream 的最佳方法是什么?

我目前正在做:

StringBuilder message = new StringBuilder("All your base");
message.Append(" are belong to us");

System.IO.MemoryStream stream = new System.IO.MemoryStream();
System.Text.ASCIIEncoding encoding = new ASCIIEncoding();
stream.Write(encoder.GetBytes(message.ToString()), 0, message.Length);
4

6 回答 6

92

不要使用 StringBuilder,如果您正在写入流,请使用StreamWriter执行此操作:

using (var memoryStream = new MemoryStream())
using (var writer = new StreamWriter(memoryStream ))
{
    // Various for loops etc as necessary that will ultimately do this:
    writer.Write(...);
}
于 2010-02-10T11:38:45.970 回答
18

那是最好的方法。其他明智的损失 StringBuilder 并使用如下内容:

using (MemoryStream ms = new MemoryStream())
{
    using (StreamWriter sw = new StreamWriter(ms, Encoding.Unicode))
    {
        sw.WriteLine("dirty world.");
    }
    //do somthing with ms
}
于 2010-02-10T11:39:25.843 回答
6

也许它会有用。

var sb= new StringBuilder("All your money");
sb.Append(" are belong to us, dude.");
var myString = sb.ToString();
var myByteArray = System.Text.Encoding.UTF8.GetBytes(myString);
var ms = new MemoryStream(myByteArray);
// Do what you need with MemoryStream
于 2016-05-09T17:28:51.603 回答
5

根据您的用例,从 StringWriter 开始也可能有意义:

StringBuilder sb = null;

// StringWriter - a TextWriter backed by a StringBuilder
using (var writer = new StringWriter())
{
    writer.WriteLine("Blah");
    . . .
    sb = writer.GetStringBuilder(); // Get the backing StringBuilder out
}

// Do whatever you want with the StringBuilder
于 2013-05-22T23:39:12.600 回答
2

编辑:正如@ramon-smits 指出的那样,如果您可以访问StringBuilder.GetChunks(),您也可以访问StreamWriter.WriteAsync(StringBuilder). 所以你可以这样做:

StringBuilder stringBuilder = new StringBuilder();
// Write data to StringBuilder...
Stream stream = GetStream(); // Get output stream from somewhere.
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true))
{
    await streamWriter.WriteAsync(stringBuilder);
}

简单得多。


我最近不得不做这件事,发现这个问题的答案并不令人满意。

您可以将 StringBuilder 写入 Stream 而无需具体化整个字符串:

StringBuilder stringBuilder = new StringBuilder();
// Write data to StringBuilder...
Stream stream = GetStream(); // Get output stream from somewhere.
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true))
{
    foreach (ReadOnlyMemory<char> chunk in stringBuilder.GetChunks())
    {
        await streamWriter.WriteAsync(chunk);
    }
}

注意 此 API (StringBuilder.GetChunks()) 仅在 .NET Core 3.0 及更高版本中可用

如果此操作频繁发生,您可以通过使用 StringBuilder 对象池进一步降低 GC 压力。

于 2020-10-22T15:19:18.920 回答
1

如果您想使用 StringBuilder 之类的东西,因为它更易于传递和使用,那么您可以使用我创建的以下 StringBuilder 替代品。

它最重要的不同之处在于它允许访问内部数据,而无需先将其组装成 String 或 ByteArray。这意味着您不必加倍内存需求,也不必冒险尝试分配适合整个对象的连续内存块。

注意:我确信在List<string>()内部使用 a 会有更好的选择,但这很简单,并且被证明足以满足我的目的。

public class StringBuilderEx
{
    List<string> data = new List<string>();
    public void Append(string input)
    {
        data.Add(input);
    }
    public void AppendLine(string input)
    {
        data.Add(input + "\n");
    }
    public void AppendLine()
    {
        data.Add("\n");
    }

    /// <summary>
    /// Copies all data to a String.
    /// Warning: Will fail with an OutOfMemoryException if the data is too
    /// large to fit into a single contiguous string.
    /// </summary>
    public override string ToString()
    {
        return String.Join("", data);
    }

    /// <summary>
    /// Process Each section of the data in place.   This avoids the
    /// memory pressure of exporting everything to another contiguous
    /// block of memory before processing.
    /// </summary>
    public void ForEach(Action<string> processData)
    {
        foreach (string item in data)
            processData(item);
    }
}

现在您可以使用以下代码将全部内容转储到文件中。

var stringData = new StringBuilderEx();
stringData.Append("Add lots of data");

using (StreamWriter file = new System.IO.StreamWriter(localFilename))
{
    stringData.ForEach((data) =>
    {
        file.Write(data);
    });
}
于 2017-09-30T13:41:28.847 回答