如何从流中准确读取 n 个字节?

发布于 2024-12-05 19:31:19 字数 773 浏览 1 评论 0原文

这比我最初想象的要棘手一些。我正在尝试从流中读取 n 个字节。

Read 不包含 MSDN 声明必须返回 n 个字节,它必须返回至少 1 到 n 个字节,其中 0 字节是到达流末尾的特殊情况。

通常,我使用的东西类似于

var buf = new byte[size];
var count = stream.Read (buf, 0, size);

if (count != size) {
    buf = buf.Take (count).ToArray ();
}

yield return buf;

我希望精确的 size 字节,但根据规范 FileStream 也将被允许返回大量 1 字节块。必须避免这种情况。

解决这个问题的一种方法是有 2 个缓冲区,一个用于读取,一个用于收集块,直到我们获得请求的字节数。不过这有点麻烦。

我还查看了 BinaryReader 但其规范也没有明确说明肯定会返回 n 个字节。

澄清一下:当然,在流结束时返回的字节数可能小于 size - 这不是问题。我只是谈论没有接收到 n 个字节,即使它们在流中可用。

This is a little more tricky than I first imagined. I'm trying to read n bytes from a stream.

The MSDN claims that Read does not have to return n bytes, it just must return at least 1 and up to n bytes, with 0 bytes being the special case of reaching the end of the stream.

Typically, I'm using something like

var buf = new byte[size];
var count = stream.Read (buf, 0, size);

if (count != size) {
    buf = buf.Take (count).ToArray ();
}

yield return buf;

I'm hoping for exactly size bytes but by spec FileStream would be allowed to return a large number of 1-byte chunks as well. This must be avoided.

One way to solve this would be to have 2 buffers, one for reading and one for collecting the chunks until we got the requested number of bytes. That's a little cumbersome though.

I also had a look at BinaryReader but its spec also does not clearly state that n bytes will be returned for sure.

To clarify: Of course, upon the end of the stream the returned number of bytes may be less than size - that's not a problem. I'm only talking about not receiving n bytes even though they are available in the stream.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

把回忆走一遍 2024-12-12 19:31:20

一个稍微更可读的版本:

int offset = 0;
while (offset < count)
{
    int read = stream.Read(buffer, offset, count - offset);
    if (read == 0)
        throw new System.IO.EndOfStreamException();
    offset += read;
}

或者编写为 Stream 类的扩展方法:

public static class StreamUtils
{
    public static byte[] ReadExactly(this System.IO.Stream stream, int count)
    {
        byte[] buffer = new byte[count];
        int offset = 0;
        while (offset < count)
        {
            int read = stream.Read(buffer, offset, count - offset);
            if (read == 0)
                throw new System.IO.EndOfStreamException();
            offset += read;
        }
        System.Diagnostics.Debug.Assert(offset == count);
        return buffer;
    }
}

A slightly more readable version:

int offset = 0;
while (offset < count)
{
    int read = stream.Read(buffer, offset, count - offset);
    if (read == 0)
        throw new System.IO.EndOfStreamException();
    offset += read;
}

Or written as an extension method for the Stream class:

public static class StreamUtils
{
    public static byte[] ReadExactly(this System.IO.Stream stream, int count)
    {
        byte[] buffer = new byte[count];
        int offset = 0;
        while (offset < count)
        {
            int read = stream.Read(buffer, offset, count - offset);
            if (read == 0)
                throw new System.IO.EndOfStreamException();
            offset += read;
        }
        System.Diagnostics.Debug.Assert(offset == count);
        return buffer;
    }
}
往昔成烟 2024-12-12 19:31:20

简单地;你循环;

int read, offset = 0;
while(leftToRead > 0 && (read = stream.Read(buf, offset, leftToRead)) > 0) {
    leftToRead -= read;
    offset += read;
}
if(leftToRead > 0) throw new EndOfStreamException(); // not enough!

此后,buf 应该已经填充了来自流的正确数量的数据,否则将抛出 EOF。

Simply; you loop;

int read, offset = 0;
while(leftToRead > 0 && (read = stream.Read(buf, offset, leftToRead)) > 0) {
    leftToRead -= read;
    offset += read;
}
if(leftToRead > 0) throw new EndOfStreamException(); // not enough!

After this, buf should have been populated with exactly the right amount of data from the stream, or will have thrown an EOF.

飘然心甜 2024-12-12 19:31:20

从这里的答案中收集所有内容,我想出了以下解决方案。它依赖于源流长度。适用于 .NET core 3.1

/// <summary>
/// Copy stream based on source stream length
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="bufferSize">
/// A value that is the largest multiple of 4096 and is still smaller than the LOH threshold (85K).
/// So the buffer is likely to be collected at Gen0, and it offers a significant improvement in Copy performance.
/// </param>
/// <returns></returns>
private async Task CopyStream(Stream source, Stream destination, int bufferSize = 81920)
{
    var buffer = new byte[bufferSize];
    var offset = 0;
    while (offset < source.Length)
    {
        var leftToRead = source.Length - offset;
        var lengthToRead = leftToRead - buffer.Length < 0 ? (int)(leftToRead) : buffer.Length;
        var read = await source.ReadAsync(buffer, 0, lengthToRead).ConfigureAwait(false);
        if (read == 0)
            break;
        await destination.WriteAsync(buffer, 0, lengthToRead).ConfigureAwait(false);
        offset += read;
    }
    destination.Seek(0, SeekOrigin.Begin);
}

Getting everything together from answers here I came up with the following solution. It relies on a source stream length. Works on .NET core 3.1

/// <summary>
/// Copy stream based on source stream length
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
/// <param name="bufferSize">
/// A value that is the largest multiple of 4096 and is still smaller than the LOH threshold (85K).
/// So the buffer is likely to be collected at Gen0, and it offers a significant improvement in Copy performance.
/// </param>
/// <returns></returns>
private async Task CopyStream(Stream source, Stream destination, int bufferSize = 81920)
{
    var buffer = new byte[bufferSize];
    var offset = 0;
    while (offset < source.Length)
    {
        var leftToRead = source.Length - offset;
        var lengthToRead = leftToRead - buffer.Length < 0 ? (int)(leftToRead) : buffer.Length;
        var read = await source.ReadAsync(buffer, 0, lengthToRead).ConfigureAwait(false);
        if (read == 0)
            break;
        await destination.WriteAsync(buffer, 0, lengthToRead).ConfigureAwait(false);
        offset += read;
    }
    destination.Seek(0, SeekOrigin.Begin);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文