写入然后从MemoryStream中读取

时间:2009-08-05 10:43:03

标签: c#

我正在使用DataContractJsonSerializer,它喜欢输出到Stream。我想对串行器的输出进行顶部和尾部处理,因此我使用StreamWriter交替写入我需要的额外位。

var ser = new DataContractJsonSerializer(typeof (TValue));

using (var stream = new MemoryStream())
{   
    using (var sw = new StreamWriter(stream))
    {
        sw.Write("{");

        foreach (var kvp in keysAndValues)
        {
            sw.Write("'{0}':", kvp.Key);
            ser.WriteObject(stream, kvp.Value);
        }

        sw.Write("}");
    }

    using (var streamReader = new StreamReader(stream))
    {
        return streamReader.ReadToEnd();
    }
}

当我这样做时,我得到ArgumentException“流不可读”。

我可能在这里做错了所以所有答案都欢迎。感谢。

4 个答案:

答案 0 :(得分:107)

三件事:

  • 请勿关闭StreamWriter。这将关闭MemoryStream。你确实需要刷新作者。
  • 在阅读之前重置流的位置。
  • 如果您要直接写入流,则需要先刷新编写器。

所以:

using (var stream = new MemoryStream())
{
    var sw = new StreamWriter(stream);
    sw.Write("{");

    foreach (var kvp in keysAndValues)
    {
        sw.Write("'{0}':", kvp.Key);
        sw.Flush();
        ser.WriteObject(stream, kvp.Value);
    }    
    sw.Write("}");            
    sw.Flush();
    stream.Position = 0;

    using (var streamReader = new StreamReader(stream))
    {
        return streamReader.ReadToEnd();
    }
}

但还有另一种更简单的选择。阅读时您正在使用流进行的所有操作都将其转换为字符串。你可以更简单地做到这一点:

return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int) stream.Length);

不幸的是,如果流已关闭,MemoryStream.Length将抛出,因此您可能想要调用不关闭基础流的StreamWriter构造函数,或者只是不要关闭{ {1}}。

我很担心你直接写到流 - 什么是StreamWriter?它是XML序列化程序还是二进制序列化程序?如果它是二进制的,那么你的模型有些缺陷 - 你不应该混淆二进制和文本数据而不要非常小心。如果它是XML,您可能会发现在字符串中间最终会出现字节顺序标记,这可能会有问题。

答案 1 :(得分:7)

将内存流位置设置为开头可能会有所帮助。

 stream.Position = 0; 

但核心问题是StreamWriter在关闭时会关闭你的内存流。

只需刷新那个结束使用块的流,然后只处理它 fter 你已经从内存流中读取数据将为你解决这个问题。

您可能还想考虑使用StringWriter而不是......

using (var writer = new StringWriter())
{
    using (var sw = new StreamWriter(stream))
    {
        sw.Write("{");

        foreach (var kvp in keysAndValues)
        {
            sw.Write("'{0}':", kvp.Key);
            ser.WriteObject(writer, kvp.Value);
        }
        sw.Write("}");
    }

    return writer.ToString();
}

这将要求您的序列化WriteObject调用可以接受TextWriter而不是Stream。

答案 2 :(得分:3)

要在关闭后访问MemoryStream 的内容,请使用ToArray()GetBuffer()方法。以下代码演示了如何将内存缓冲区的内容作为UTF8编码的字符串。

byte[] buff = stream.ToArray(); 
return Encoding.UTF8.GetString(buff,0,buff.Length);

注意:ToArray()GetBuffer()更易于使用,因为ToArray()返回流的确切长度,而不是缓冲区大小(可能大于流内容)。 ToArray()复制字节。

注意:GetBuffer()ToArray()更高效,因为它不会复制字节。您需要考虑流长度而不是缓冲区大小,在缓冲区末尾处理可能未定义的尾随字节。如果流大小大于80000字节,强烈建议使用GetBuffer(),因为ToArray副本将在大对象堆上分配,因为它的生命周期可能会成为问题。

也可以按如下方式克隆原始MemoryStream,以便通过StreamReader访问它,例如

using (MemoryStream readStream = new MemoryStream(stream.ToArray()))
{
...
}

理想的解决方案是在关闭之前访问原始的MemoryStream,如果可能的话。

答案 3 :(得分:1)

只是一个疯狂的猜测:也许你需要冲洗算术者?可能系统发现写入“待定”。通过刷新,您确定该流包含所有书写字符并且可读。