使用Stream.Read()和BinaryReader.Read()来处理二进制流

时间:2013-06-11 12:02:49

标签: c# .net stream

使用二进制流(即byte[]数组)时,使用BinaryReaderBinaryWriter的要点似乎是简化从流中读取/写入原始数据类型,使用ReadBoolean()等方法并考虑编码。这是整个故事吗?如果直接使用Stream,而不使用BinaryReader/BinaryWriter,是否存在固有的优势或劣势?大多数方法,例如Read(),在两个类中似乎都是相同的,我的猜测是它们在下面工作相同。

考虑一个以两种不同方式处理二进制文件的简单示例(编辑:我意识到这种方式无效,可以使用缓冲区,它只是一个示例):

// Using FileStream directly
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    int value = 0;
    while ((value = stream.ReadByte()) != -1)
    {
        Console.WriteLine(value);
    }
}


// Using BinaryReader
using (BinaryReader reader = new BinaryReader(FileStream fs = new FileStream("file.dat", FileMode.Open)))
{
    // Read bytes and interpret them as ints
    byte value = 0;    
    while (reader.BaseStream.Position < reader.BaseStream.Length)
    {
        value = reader.ReadByte();
        Console.WriteLine(Convert.ToInt32(value));
    }
}

输出将是相同的,但内部发生了什么(例如从OS的角度来看)?是 - 一般来说 - 重要的是使用哪种实现?如果您不需要他们提供的额外方法,是否有使用BinaryReader/BinaryWriter的目的? 对于这种特定情况,MSDN就Stream.ReadByte()

说明了这一点
  

Stream上的默认实现创建了一个新的单字节数组   然后调用Read。虽然这是正式的,但确实如此   低效的。

使用GC.GetTotalMemory(),第一种方法似乎分配的空间是第二种方法的2倍,但如果使用更通用的Stream.Read()方法,AFAIK就不应该这样(例如使用缓冲区读取块。不过,在我看来,这些方法/接口可以轻松统一......

3 个答案:

答案 0 :(得分:14)

不,两种方法之间没有本质区别。额外的Reader增加了一些缓冲,所以你不应该混合它们。但是,不要期望任何重大的性能差异,它们都由实际的I / O支配。

所以,

  • 当您(仅)byte[]移动时使用流。在许多流媒体场景中很常见。
  • 当您要处理任何其他基本类型(包括简单byte)数据时,请使用BinaryWriter和BinaryReader。它们的主要目的是将内置框架类型转换为byte[]

答案 1 :(得分:10)

一个很大的区别是如何缓冲I / O.如果您在此处或那里只编写/读取几个字节,BinaryWriter/BinaryReader将运行良好。但是如果你必须读取数据的MB,那么一次读取一个byteInt32等等将会有点慢。你可以改为阅读更大的块并从那里解析。

示例:

// Using FileStream directly with a buffer
using (FileStream stream = new FileStream("file.dat", FileMode.Open))
{
    // Read bytes from stream and interpret them as ints
    byte[] buffer = new byte[1024];
    int count;
    // Read from the IO stream fewer times.
    while((count = stream.Read(buffer, 0, buffer.Length)) > 0)
        for(int i=0; i<count; i++)
           Console.WriteLine(Convert.ToInt32(buffer[i]));
}

现在这有点偏离主题......但我会把它扔出去: 如果你想变得非常狡猾...并且真的给自己一个性能提升......(虽然,它可能被认为是危险的)而不是解析每个Int32,你可以使用{{1一次性完成所有这些操作}}

另一个例子:

Buffer.BlockCopy()

关于这个例子需要注意几点:这个每个Int32需要4个字节而不是一个...所以它会产生不同的结果。您也可以为Int32以外的其他数据类型执行此操作,但许多人会认为编组应该在您的脑海中。 (我只是想提出一些值得考虑的事情......)

答案 2 :(得分:0)

两个代码都执行相同的操作,即。 ReadByte(),它以字节数组结尾,因此两种方法的结果相同(来自同一文件)。
OS的实现(内部差异)是流被缓存在虚拟内存中,例如。如果您是通过流在网络上传输文件,则仍将剩余系统内存用于其他(多)任务。
对于字节数组,整个文件在传输到磁盘(文件创建)或其他流之前将存储在内存中,因此不建议用于大文件。

这里有一些关于通过网络传输二进制数据的讨论:
When to use byte array, and when to use stream?

@Jason C和@Jon Skeet在这里提出了一个要点:
Why do most serializers use a stream instead of a byte array?

我注意到,当我继续使用Win 10机器(4 GB RAM)通过System.Net.Http.Httpclient GetByteArrayAsync方法(与GetStreamAsync)传输文件时,有时会跳过5MB以上的文件,而无需等待传输完成。

PS:.Net 4.0字节数组限于2GB