web-dev-qa-db-ja.com

メモリストリームから文字列への読み取り

オブジェクトをXml文字列に書き込み、その文字列を取得してDBに保存しようとしています。しかし、最初に文字列を取得する必要があります...

    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」で満たされます!ここで何が間違っているのかわかりません。

37

stream.Readの結果をチェックした場合、ストリームを巻き戻していないため、何も読み取っていないことがわかります。 (stream.Position = 0;でこれを行うことができます。)ただし、ToArrayを呼び出す方が簡単です:

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

streamのタイプをStreamからMemoryStreamに変更する必要がありますが、作成したのと同じメソッドであるため、これは問題ありません。)

または、さらに簡単に、StringWriterの代わりにStreamWriterを使用します。 UTF-16の代わりにUTF-8を使用する場合は、サブクラスを作成する必要がありますが、それは非常に簡単です。例については this answer をご覧ください。

私はあなたがExceptionをキャッチしている方法とと仮定して、それが無害なことを意味することを心配しています-何でもログに記録します。 usingステートメントは、通常、明示的なfinallyブロックを記述するよりもクリーンであることに注意してください。

75
Jon Skeet
string result = System.Text.Encoding.UTF8.GetString(fs.ToArray());
19
Farzad J
string result = Encoding.UTF8.GetString((stream as MemoryStream).ToArray());
6
evorios

非常に長いストリーム長の場合、 Large Object Heap によるメモリリークの危険があります。つまり、stream.ToArrayによって作成されたバイトバッファーは、ヒープメモリにメモリストリームのコピーを作成し、予約メモリの複製につながります。 StreamReaderTextWriterを使用し、charバッファーのチャンクでストリームを読み取ることをお勧めします。

Netstandard2.0 System.IO.StreamReaderにはメソッドがあります ReadBlock

このメソッドを使用して、Streamのインスタンス(StreamはMemoryStreamのスーパーなので、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を回避するために、 Recyclable Memory stream を使用できます。これは関連する library

2
George Kargakis