【问题标题】:Write StringBuilder to Stream将 StringBuilder 写入 Stream
【发布时间】:2011-01-15 05:43:31
【问题描述】:

将 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);

【问题讨论】:

    标签: c# stream stringbuilder


    【解决方案1】:

    不要使用 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(...);
    }
    

    【讨论】:

    • 如果他需要在写之前将字符串用于其他东西,这是不可行的。
    • 绝对正确,但他当然没有说这是要求,所以我没有做任何假设。
    • 还记得在从流中读取之前Flush and Seek
    【解决方案2】:

    这是最好的方法。否则会丢失 StringBuilder 并使用以下内容:

    using (MemoryStream ms = new MemoryStream())
    {
        using (StreamWriter sw = new StreamWriter(ms, Encoding.Unicode))
        {
            sw.WriteLine("dirty world.");
        }
        //do somthing with ms
    }
    

    【讨论】:

      【解决方案3】:

      也许会有用。

      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
      

      【讨论】:

      • 先复制到字符串,再复制到字节数组,最后再复制到流?真的吗?当然,这可以简洁地完成,而无需制作任何中间副本。 (如果字符串生成器是 2GB 怎么办?)
      【解决方案4】:

      根据您的用例,从 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
      

      【讨论】:

      【解决方案5】:

      编辑:正如@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压力。

      【讨论】:

      • 这是从 StringBuilder 中提取数据的好方法。一个警告是这不是线程安全的,所以你应该在这段代码周围加一个锁。 docs.microsoft.com/en-us/dotnet/api/…
      • 我觉得不需要这个,你可以streamWriter.WriteAsync(stringBuilder)
      【解决方案6】:

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

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

      注意:我确信有比在内部使用 List&lt;string&gt;() 更好的选择,但这很简单,并且证明对我的目的来说已经足够了。

      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);
          });
      }
      

      【讨论】:

        猜你喜欢
        • 2022-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-14
        • 1970-01-01
        • 2017-04-19
        • 2018-03-13
        • 1970-01-01
        相关资源
        最近更新 更多