从输入流创建字节数组的首选方法是什么?
下面是我目前使用。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);
}
读写流的块仍然是一个更好的主意吗?
当前回答
将两个投票最多的答案组合成一个扩展方法:
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();
}
}
其他回答
将两个投票最多的答案组合成一个扩展方法:
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();
}
}
这真的取决于你是否可以信任。对于许多流,你只是不知道会有多少数据。在这种情况下——在。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()。工作。
我也许应该解释一下为什么我的答案比其他人的长。流。Read不能保证它会读取所有要求的内容。例如,如果您正在从网络流中读取数据,它可能会读取一个数据包的值,然后返回,即使很快会有更多的数据。主题。Read将一直进行到流的结束或指定的大小,但您仍然必须知道开始时的大小。
The above method will keep reading (and copying into a MemoryStream) until it runs out of data. It then asks the MemoryStream to return a copy of the data in an array. If you know the size to start with - or think you know the size, without being sure - you can construct the MemoryStream to be that size to start with. Likewise you can put a check at the end, and if the length of the stream is the same size as the buffer (returned by MemoryStream.GetBuffer) then you can just return the buffer. So the above code isn't quite optimised, but will at least be correct. It doesn't assume any responsibility for closing the stream - the caller should do that.
有关更多信息(以及另一种实现),请参阅本文。
只是想指出,如果你有一个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);
}
}
只是我的几毛钱……我经常使用的做法是将这些方法组织成一个自定义助手
public static class StreamHelpers
{
public static byte[] ReadFully(this Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
将命名空间添加到配置文件中,并在任何地方使用它
如果流支持Length属性,则可以直接创建字节数组。其优点是MemoryStream。ToArray创建两次数组。另外,缓冲区中可能还有一些未使用的额外字节。此解决方案分配所需的精确数组。如果流不支持Length属性,它将抛出NotSupportedException异常。
同样值得注意的是,数组不能大于int.MaxValue。
public static async Task<byte[]> ToArrayAsync(this Stream stream)
{
var array = new byte[stream.Length];
await stream.ReadAsync(array, 0, (int)stream.Length);
return array;
}
根据流是否支持搜索在两个版本之间切换的完整代码。它包括检查位置和不可靠的长度。这可能会略微降低速度。在我的测试中,ToArrayAsyncDirect比ToArrayAsyncGeneral快3倍。
public static class StreamExtensions
{
public static readonly byte[] TempArray = new byte[4];
/// <summary>
/// Converts stream to byte array.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Stream data as array</returns>
/// <returns>Binary data from stream in an array</returns>
public static async Task<byte[]> ToArrayAsync(this Stream stream, CancellationToken cancellationToken)
{
if (!stream.CanRead)
{
throw new AccessViolationException("Stream cannot be read");
}
if (stream.CanSeek)
{
return await ToArrayAsyncDirect(stream, cancellationToken);
}
else
{
return await ToArrayAsyncGeneral(stream, cancellationToken);
}
}
/// <summary>
/// Converts stream to byte array through MemoryStream. This doubles allocations compared to ToArrayAsyncDirect.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns></returns>
private static async Task<byte[]> ToArrayAsyncGeneral(Stream stream, CancellationToken cancellationToken)
{
using MemoryStream memoryStream = new MemoryStream();
await stream.CopyToAsync(memoryStream, cancellationToken);
return memoryStream.ToArray();
}
/// <summary>
/// Converts stream to byte array without unnecessary allocations.
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Stream data as array</returns>
/// <exception cref="ArgumentException">Thrown if stream is not providing correct Length</exception>
private static async Task<byte[]> ToArrayAsyncDirect(Stream stream, CancellationToken cancellationToken)
{
if (stream.Position > 0)
{
throw new ArgumentException("Stream is not at the start!");
}
var array = new byte[stream.Length];
int bytesRead = await stream.ReadAsync(array, 0, (int)stream.Length, cancellationToken);
if (bytesRead != array.Length ||
await stream.ReadAsync(TempArray, 0, TempArray.Length, cancellationToken) > 0)
{
throw new ArgumentException("Stream does not have reliable Length!");
}
return array;
}
}