Подтвердить что ты не робот

Запись на чтение с MemoryStream

Я использую 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 "Поток не читаем".

Я, наверное, все испортил, поэтому все ответы приветствуются. Спасибо.

4b9b3361

Ответ 1

Три вещи:

  • Не закрывайте StreamWriter. Это закроет MemoryStream. Однако вам нужно очистить писателя.
  • Reset положение потока перед чтением.
  • Если вы собираетесь писать напрямую в поток, сначала нужно сначала очистить запись.

Итак:

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, вы можете обнаружить, что в конце строки вы заканчиваете отметки байтового порядка, что может быть проблематичным.

Ответ 2

установка позиции потоков памяти в начало может помочь.

 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.

Ответ 3

Чтобы получить доступ к содержимому файла MemoryStream после его закрытия, используйте методы ToArray() или GetBuffer(). Следующий код демонстрирует, как получить содержимое буфера памяти как кодированную строку UTF8.

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

Примечание. ToArray() проще использовать, чем GetBuffer(), потому что ToArray() возвращает точную длину потока, а не размер буфера (который может быть больше, чем содержимое потока). ToArray() делает копию байтов.

Примечание: GetBuffer() более совершенен, чем ToArray(), так как он не делает копию байтов. Вам нужно позаботиться о возможных байтах undefined в конце буфера, считая длину потока, а не размер буфера. Использование GetBuffer() настоятельно рекомендуется, если размер потока больше 80000 байт, потому что копия ToArray будет выделена в кучке больших объектов, где ее жизненное время может стать проблематичным.

Также возможно клонировать исходный MemoryStream следующим образом, чтобы облегчить доступ к нему через StreamReader, например.

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

Идеальное решение - доступ к исходному MemoryStream до его закрытия, если это возможно.

Ответ 4

Просто дикая догадка: может быть, вам нужно сбросить потоковый блок? Возможно, система видит, что есть записи "ожидающие". С помощью смыва вы точно знаете, что поток содержит все написанные символы и доступен для чтения.