如何从输入流中创建字节数组是首选的方法?
这是我目前使用 .NET 3.5 的解决方案。
Stream s;
byte[] b;
using (BinaryReader br = new BinaryReader(s))
{
b = br.ReadBytes((int)s.Length);
}
阅读和写入流的块仍然是一个更好的主意吗?
如何从输入流中创建字节数组是首选的方法?
这是我目前使用 .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的答案。在许多情况下,CopyTo
中的异常相当普遍,会使MemoryStream
未被处理,因此可以通过使用"using"来改进Sandip的代码。
public static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
input
是否已经是一个 MemoryStream
并进行短路处理可能是值得的。我知道调用者传递一个 MemoryStream
是愚蠢的,但是... - Jodrell只想指出如果您已经有一个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);
}
}
MemoryStream
的流。当然,这个例子显然是不完整的,因为它使用了一个未初始化的变量。 - Roman Starkovstream.Seek(1L, SeekOrigin.Begin)
,如果该流是内存流,则读取到的字节数将比其他流多1个字节。如果调用方期望从当前位置读取到流的末尾,则不应使用CopyTo
或ToArray()
;在大多数情况下这不会成为问题,但如果调用方不知道这种古怪的行为,他们会感到困惑。 - leatMemoryStream ms = new MemoryStream();
file.PostedFile.InputStream.CopyTo(ms);
var byts = ms.ToArray();
ms.Dispose();
这只是我的一些个人看法... 我经常使用的做法是将方法组织成自定义助手函数。
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
在配置文件中添加命名空间,并在任何需要的地方使用它。
Stream
上不存在 CopyTo
方法。 - Tim你可以简单地使用MemoryStream类的ToArray()方法,例如-
MemoryStream ms = (MemoryStream)dataInStream;
byte[] imageBytes = ms.ToArray();
dataInStream
已经是一个MemoryStream
时,这才起作用。 - Fowl你甚至可以通过扩展使它变得更加花哨:
namespace Foo
{
public static class Extensions
{
public static byte[] ToByteArray(this Stream stream)
{
using (stream)
{
using (MemoryStream memStream = new MemoryStream())
{
stream.CopyTo(memStream);
return memStream.ToArray();
}
}
}
}
}
然后像普通方法一样调用它:
byte[] arr = someStream.ToByteArray()
在Bob的代码中(即提问者的代码),我遇到了编译时错误。Stream.Length是long类型,而BinaryReader.ReadBytes需要一个整数参数。在我的情况下,我不希望处理需要长精度的流,因此我使用以下代码:
Stream s;
byte[] b;
if (s.Length > int.MaxValue) {
throw new Exception("This stream is larger than the conversion algorithm can currently handle.");
}
using (var br = new BinaryReader(s)) {
b = br.ReadBytes((int)s.Length);
}
如果有人喜欢,这里有一个仅适用于.NET 4+的解决方案,可以形成扩展方法而不需要在MemoryStream上进行不必要的Dispose调用。 这是一个无望的微小优化,但值得注意的是,未处理MemoryStream的Dispose并不是一个真正的失败。
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
var ms = new MemoryStream();
input.CopyTo(ms);
return ms.ToArray();
}
}
将两个最受欢迎的答案组合成扩展方法:
public static byte[] ToByteArray(this Stream stream)
{
if (stream is MemoryStream)
return ((MemoryStream)stream).ToArray();
else
{
using MemoryStream ms = new();
stream.CopyTo(ms);
return ms.ToArray();
}
}
BinaryWriter/Reader
写入/读取字符串时,UTF-8才会起作用。在您提供的链接中,他们将数字和字符串写入二进制文件。当写入字符串时,我相信首先写入长度,然后是UTF-8编码的字符串。但是,如果您只读取字节,则编码应该没有影响,因此回答您的问题,如果您正在读取图像或其他“实际”二进制数据,则不会出现此问题。 - nurchi