【问题标题】:Reading from memory stream to string从内存流读取到字符串
【发布时间】:2012-05-13 20:13:17
【问题描述】:

我正在尝试将一个对象写入一个 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'!不知道我在这里做错了什么。

【问题讨论】:

标签: c# xml-serialization xmlserializer memorystream


【解决方案1】:

如果您检查了stream.Read 的结果,您会发现它没有读取任何内容——因为您还没有倒回流。 (您可以使用stream.Position = 0; 来执行此操作。)但是,调用ToArray 会更容易:

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

(您需要将stream 的类型从Stream 更改为MemoryStream,但这没关系,因为它与您创建它的方法相同。)

或者 - 甚至更简单 - 只需使用 StringWriter 而不是 StreamWriter。如果您想使用 UTF-8 而不是 UTF-16,则需要创建一个子类,但这很容易。示例见this answer

我担心你只是在捕捉Exception 并且假设顺便说一句,这意味着一些无害的东西 - 甚至没有记录任何东西。请注意,using 语句通常比编写显式 finally 块更干净。

【讨论】:

  • 谢谢 :) 。这是快速的工作。我知道使用块更常见。但我非常喜欢这给我的平坦度,没有嵌套使用。这只是一种风格。我不知道有一个 StringWriter,似乎更容易,但我喜欢指定编码的能力。我可能会为此添加一个重载,而不是一个新类。
  • @tigerswithguitars:您究竟打算如何为StringWriter 本身添加重载?新类的重点是StringWriter 总是从其TextWriter.Encoding 属性返回UTF-16;额外的类只是覆盖该属性。
  • 对不起,我的措辞令人困惑。我将在我自己的方法中添加一个重载来指定编码。
  • @tigerswithguitars:对,没关系。您仍然需要添加额外的 StringWriter 类,但这只是为了在方法中使用
  • 我刚读完你的书……今天有人投了赞成票。我忘记了你已经回答了这个问题。惊人的巧合。此外,《深入了解 C#》是我读过的最好的语言书籍,非常棒。
【解决方案2】:
string result = System.Text.Encoding.UTF8.GetString(fs.ToArray());

【讨论】:

  • 虽然此代码示例可能会回答这个问题,但最好在此处包含一些对答案的基本解释。
  • 这里的stem 是什么?如果是一个变量,你是如何实例化它的? fs 的同样问题。
  • 用户误用stem代替System
【解决方案3】:
string result = Encoding.UTF8.GetString((stream as MemoryStream).ToArray());

【讨论】:

  • 我会质疑在这里使用as - 它并不能保护您免受任何伤害。首先,您知道流是MemoryStream 无论如何,另外,即使不是,将null 传递给GetString 也会引发异常,因此您不妨首先使用显式转换。
  • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
【解决方案4】:

在流长度非常大的情况下,由于Large Object Heap,存在内存泄漏的危险。即由 stream.ToArray 创建的字节缓冲区在堆内存中创建内存流的副本,从而导致保留内存的重复。我建议使用StreamReaderTextWriter 并以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;
}

注意。由于使用了MemoryStream,内存泄漏的危害并没有完全消除,可能会导致大内存流实例(memoryStreamInstance.Size >85000 bytes)的内存泄漏。您可以使用Recyclable Memory stream,以避免LOH。这是相关的library

【讨论】:

  • 是的 - 使用 StreamReader - 但不是因为内存泄漏,而是因为它只是最好的方法!
猜你喜欢
  • 1970-01-01
  • 2021-09-13
  • 2015-11-01
  • 1970-01-01
  • 2013-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多