C# 写入然后从 MemoryStream 读取

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1232443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 14:00:04  来源:igfitidea点击:

Writing to then reading from a MemoryStream

c#

提问by Gaz

I'm using DataContractJsonSerializer, which likes to output to a Stream. I want to top-and-tail the outputs of the serializer so I was using a StreamWriter to alternately write in the extra bits I needed.

我正在使用DataContractJsonSerializer,它喜欢输出到流。我想对序列化器的输出进行顶部和尾部处理,因此我使用 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();
    }
}

When I do this I get an ArgumentException"Stream was not readable".

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

I'm probably doing all sorts wrong here so all answers welcome. Thanks.

我可能在这里做错了各种各样的事情,所以欢迎所有答案。谢谢。

采纳答案by Jon Skeet

Three things:

三件事:

  • Don't close the StreamWriter. That will close the MemoryStream. You do need to flush the writer though.
  • Reset the position of the stream before reading.
  • If you're going to write directly to the stream, you need to flush the writer first.
  • 不要关闭StreamWriter. 这将关闭MemoryStream. 不过,您确实需要刷新作家。
  • 读取前重置流的位置。
  • 如果要直接写入流,则需要先刷新写入器。

So:

所以:

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

There's another simpler alternative though. All you're doing with the stream when reading is converting it into a string. You can do that more simply:

不过,还有另一种更简单的选择。读取时您对流所做的一切就是将其转换为字符串。你可以更简单地做到这一点:

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

Unfortunately MemoryStream.Lengthwill throw if the stream has been closed, so you'd probably want to call the StreamWriterconstructor that doesn't close the underlying stream, or just don't close the StreamWriter.

不幸的是,MemoryStream.Length如果流已关闭,则会抛出异常,因此您可能想要调用StreamWriter不关闭底层流的构造函数,或者只是不关闭StreamWriter.

I'm concerned by you writing directly to the the stream - what is ser? Is it an XML serializer, or a binary one? If it's binary, your model is somewhat flawed - you shouldn't mix binary and text data without being very careful about it. If it's XML, you may find that you end up with byte-order marks in the middle of your string, which could be problematic.

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

回答by ShuggyCoUk

setting the memory streams position to the beginning might help.

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

 stream.Position = 0; 

But the core problem is that the StreamWriter is closing your memory stream when it is closed.

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

Simply flushing that stream where you end the using block for it and only disposing of it fteryou have read the data out of the memory stream will solve this for you.

简单地冲洗你在哪里结束的使用块,并仅处置它是流压脚提升你读出来的数据内存流将解决这个给你的。

You may also want to consider using a StringWriterinstead...

您可能还想考虑使用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();
}

This would require your serialization WriteObject call can accept a TextWriter instead of a Stream.

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

回答by Gertjan

Just a wild guess: maybe you need to flush the streamwriter? Possibly the system sees that there are writes "pending". By flushing you know for sure that the stream contains all written characters and is readable.

只是一个疯狂的猜测:也许您需要刷新流编写器?可能系统看到有写入“待处理”。通过刷新,您可以确定该流包含所有写入的字符并且是可读的。

回答by Simon Giles

To access the content of a MemoryStream after it has been closeduse the ToArray()or GetBuffer()methods. The following code demonstrates how to get the content of the memory buffer as a UTF8 encoded string.

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

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

Note: ToArray()is simpler to use than GetBuffer()because ToArray()returns the exact length of the stream, rather than the buffer size (which might be larger than the stream content). ToArray()makes a copy of the bytes.

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

Note: GetBuffer()is more performant than ToArray(), as it doesn't make a copy of the bytes. You do need to take care about possible undefined trailing bytes at the end of the buffer by considering the stream length rather than the buffer size. Using GetBuffer()is strongly advised if stream size is larger than 80000 bytes because the ToArray copy would be allocated on the Large Object Heap where it's lifetime can become problematic.

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

It is also possible to clone the original MemoryStream as follows, to facilitate accessing it via a StreamReader e.g.

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

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

The ideal solution is to access the original MemoryStream before it has been closed, if possible.

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