簡體   English   中英

寫入然后從 MemoryStream 讀取

[英]Writing to then reading from a MemoryStream

我正在使用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();
    }
}

當我這樣做時,我得到一個ArgumentException “Stream 不可讀”。

我可能在這里做錯了各種各樣的事情,所以歡迎所有答案。 謝謝。

三件事:

  • 不要關閉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構造函數,或者只是不關閉StreamWriter

我擔心您直接寫入流 - 什么是ser 它是 XML 序列化程序還是二進制序列化程序? 如果它是二進制的,那么你的模型就有些缺陷——你不應該在沒有非常小心的情況下混合二進制和文本數據。 如果是 XML,您可能會發現在字符串的中間會出現字節順序標記,這可能會出現問題。

將內存流位置設置為開頭可能會有所幫助。

 stream.Position = 0; 

但核心問題是 StreamWriter 在關閉時正在關閉您的內存流。

簡單地沖洗你在哪里結束的使用塊,並僅處置它壓腳提升你讀出來的數據內存流將解決這個給你那流。

您可能還想考慮使用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。

在關閉后訪問 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。

只是一個瘋狂的猜測:也許您需要刷新流編寫器? 可能系統看到有寫入“待處理”。 通過刷新,您可以確定該流包含所有寫入的字符並且是可讀的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM