Запись на чтение с 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
"Поток не читаем".
Я, наверное, все испортил, поэтому все ответы приветствуются. Спасибо.
Ответы
Ответ 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
Просто дикая догадка: может быть, вам нужно сбросить потоковый блок? Возможно, система видит, что есть записи "ожидающие". С помощью смыва вы точно знаете, что поток содержит все написанные символы и доступен для чтения.