Чтение из потока памяти в строку

Я пытаюсь записать объект в строку Xml, взять эту строку и сохранить ее в БД. Но сначала мне нужно получить строку ...

    private static readonly Encoding LocalEncoding = Encoding.UTF8;

    public static string SaveToString<T> (T settings)
    {
        Stream stream = null;
        TextWriter writer = null;
        string settingsString = null;

        try
        {
            stream = new MemoryStream();

            var serializer = new XmlSerializer(typeof(T));

            writer = new StreamWriter(stream, LocalEncoding);

            serializer.Serialize(writer, settings);

            var buffer = new byte[stream.Length];

            stream.Read(buffer, 0, (int)stream.Length);

            settingsString = LocalEncoding.GetString(buffer);
        }
        catch(Exception ex)
        {
            // If the action cancels we don't want to throw, just return null.
        }
        finally
        {
            if (stream != null)
                stream.Close();

            if(writer != null)
                writer.Close();
        }

        return settingsString;
    }

Кажется, это работает, поток заполняется байтами. Но когда я прихожу, чтобы прочитать его обратно в буфер, а затем в строку ... буфер заполняется «0»! Не уверен, что я здесь делаю не так, ребята.


person tigerswithguitars    schedule 04.05.2012    source источник


Ответы (4)


Если бы вы проверили результаты stream.Read, вы бы увидели, что он ничего не читал, потому что вы не перемотали поток. (Вы можете сделать это с помощью stream.Position = 0;.) Однако проще просто вызвать ToArray:

settingsString = LocalEncoding.GetString(stream.ToArray());

(Вам нужно будет изменить тип stream с Stream на MemoryStream, но это нормально, поскольку он используется в том же методе, в котором вы его создали.)

В качестве альтернативы - и даже проще - просто используйте StringWriter вместо StreamWriter. Вам нужно будет создать подкласс, если вы хотите использовать UTF-8 вместо UTF-16, но это довольно просто. См. Пример в этом ответе.

Меня беспокоит то, как вы просто ловите Exception и предполагаете, что это означает что-то безобидное, кстати, - даже не регистрируя ничего. Обратите внимание, что операторы using обычно чище, чем явные блоки finally.

person Jon Skeet    schedule 04.05.2012
comment
Спасибо :) . Это быстрая работа. Я знаю, что блоки using более обычны. Но мне очень нравится плоскость, которую это дает мне, без вложенного использования. Это всего лишь стиль. Я не знал, что существует StringWriter, кажется, намного проще, но мне нравится возможность указывать кодировку. Я, наверное, добавлю для этого перегрузку, а не новый класс. - person tigerswithguitars; 04.05.2012
comment
@tigerswithguitars: Как именно вы собираетесь добавить перегрузку к самому StringWriter? Суть нового класса в том, что StringWriter всегда возвращает UTF-16 из своего свойства TextWriter.Encoding; дополнительный класс просто переопределяет это свойство. - person Jon Skeet; 04.05.2012
comment
извините, моя формулировка запуталась. Я добавлю перегрузку в свой собственный метод, чтобы указать кодировку. - person tigerswithguitars; 04.05.2012
comment
@tigerswithguitars: Хорошо, все в порядке. Вам все равно потребуется добавить дополнительный класс StringWriter, но это только для использования внутри метода. - person Jon Skeet; 04.05.2012
comment
Я только что закончил вашу книгу ... и кто-то проголосовал за это сегодня. Я забыл, что Ты ответил на это. Удивительное совпадение. Кроме того, C # in Depth - САМАЯ ЛУЧШАЯ языковая книга, которую я когда-либо читал, потрясающий материал. - person tigerswithguitars; 26.02.2014
comment
@tigerswithguitars: Очень рад, что вам понравилось :) - person Jon Skeet; 26.02.2014

В случае очень большой длины потока существует опасность утечки памяти из-за Куча больших объектов. т.е. байтовый буфер, созданный stream.ToArray, создает копию потока памяти в памяти Heap, что приводит к дублированию зарезервированной памяти. Я бы посоветовал использовать StreamReader, TextWriter и читать поток кусками по char буферам.

В netstandard2.0 System.IO.StreamReader имеется метод ReadBlock

вы можете использовать этот метод для чтения экземпляра Stream (также экземпляра MemoryStream, поскольку Stream является суперпользователем для MemoryStream):

private static string ReadStreamInChunks(Stream stream, int chunkLength)
{
    stream.Seek(0, SeekOrigin.Begin);
    string result;
    using(var textWriter = new StringWriter())
    using (var reader = new StreamReader(stream))
    {
        var readChunk = new char[chunkLength];
        int readChunkLength;
        //do while: is useful for the last iteration in case readChunkLength < chunkLength
        do
        {
            readChunkLength = reader.ReadBlock(readChunk, 0, chunkLength);
            textWriter.Write(readChunk,0,readChunkLength);
        } while (readChunkLength > 0);

        result = textWriter.ToString();
    }

    return result;
}

NB. Опасность утечки памяти не устранена полностью из-за использования MemoryStream, что может привести к утечке памяти для экземпляра большого потока памяти (memoryStreamInstance.Size> 85000 байт). Вы можете использовать повторно используемый поток памяти, чтобы чтобы избежать LOH. Это соответствующая библиотека.

person George Kargakis    schedule 13.09.2018
comment
Да, используйте StreamReader - но не из-за утечки памяти, а потому, что это лучший способ сделать это! - person Graham Laight; 08.04.2021

person    schedule
comment
Хотя этот пример кода, возможно, может ответить на вопрос, было бы предпочтительнее включить здесь некоторые существенные пояснения к ответу. - person oɔɯǝɹ; 29.01.2015
comment
Что здесь stem? Если это переменная, как вы ее создали? Тот же вопрос для fs. - person Nicolas Raoul; 02.09.2015
comment
Пользователь по ошибке использовал ствол вместо системы. - person Prasanth; 21.01.2016

person    schedule
comment
Я бы поставил под сомнение использование as здесь - он вас ни от чего не защищает. Во-первых, вы знаете, что поток в любом случае является MemoryStream, плюс, даже если это не так, передача null в GetString вызовет исключение, поэтому вы также можете использовать явное приведение в первую очередь. - person Simon MᶜKenzie; 20.10.2015
comment
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, улучшает его долгосрочную ценность. - person ryanyuyu; 20.10.2015