协慌网

登录 贡献 社区

从流创建字节数组

从输入流创建字节数组的首选方法是什么?

这是我目前使用. NET 3.5 的解决方案。

Stream s;
byte[] b;

using (BinaryReader br = new BinaryReader(s))
{
    b = br.ReadBytes((int)s.Length);
}

读取和写入流的块是否仍然是一个更好的主意?

答案

这真的取决于你是否可以信任s.Length 。对于许多流,您只是不知道将有多少数据。在这种情况下 - 在. NET 4 之前 - 我会使用这样的代码:

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

使用. NET 4 及更高版本,我将使用Stream.CopyTo ,它基本上等同于我的代码中的循环 - 创建MemoryStream ,调用stream.CopyTo(ms)然后返回ms.ToArray() 。任务完成。

我或许应该解释为什么我的回答比其他人长。 Stream.Read并不保证它会读取它所要求的所有内容。例如,如果您正在从网络流中读取数据,它可能会读取一个数据包的值,然后返回,即使很快会有更多数据。 BinaryReader.Read将继续运行直到流的末尾或您指定的大小,但您仍然必须知道要开始的大小。

上面的方法将继续读取(并复制到MemoryStream ),直到它用完数据。然后它要求MemoryStream返回数组中的数据副本。如果您知道要开始的大小 - 或者认为您知道大小,而不确定 - 您可以将MemoryStream构造为该大小。同样,您可以在结尾处进行检查,如果流的长度与缓冲区的大小相同(由MemoryStream.GetBuffer返回),那么您只需返回缓冲区即可。所以上面的代码并没有得到很好的优化,但至少是正确的。关闭流不承担任何责任 - 调用者应该这样做。

有关更多信息(以及替代实现),请参阅此文章

虽然 Jon 的回答是正确的,但他正在重写已经存在于CopyTo代码。因此. Net 4 使用 Sandip 的解决方案,但对于. Net 的早期版本使用 Jon 的答案。 Sandip 的代码将通过使用 “使用” 来改进,因为在许多情况下, CopyTo中的异常很可能会使MemoryStream不被处理掉。

public static byte[] ReadFully(Stream input)
{
    using (MemoryStream ms = new MemoryStream())
    {
        input.CopyTo(ms);
        return ms.ToArray();
    }
}

只是想指出,如果你有一个 MemoryStream 你已经有了memorystream.ToArray()

此外,如果您正在处理未知或不同子类型的流并且您可以接收MemoryStream ,则可以针对这些情况继续使用所述方法,并仍然使用其他人接受的答案,如下所示:

public static byte[] StreamToByteArray(Stream stream)
{
    if (stream is MemoryStream)
    {
        return ((MemoryStream)stream).ToArray();                
    }
    else
    {
        // Jon Skeet's accepted answer 
        return ReadFully(stream);
    }
}