从 BufferedStream.Read() 返回的字节数组是否有可能具有不同的长度?

Is it possible for byte arrays, returned from BufferedStream.Read(), to have different lengths?

提问人:notarobot 提问时间:2/25/2023 最后编辑:notarobot 更新时间:2/25/2023 访问量:130

问:

我们的 repo 中有这个旧代码:

    bool BufferByteCompareFiles(string filePath1, string filePath2)
    {
        int bufferCapacity = 0x400000;
        var firstFile = File.OpenRead(filePath1);
        var secondFile = File.OpenRead(filePath2);

        using var firstStream = new BufferedStream(firstFile);
        using var secondStream = new BufferedStream(secondFile);

        if (firstFile.Length != secondFile.Length)
            return false;

        var firstBuffer = new byte[bufferCapacity];
        var secondBuffer = new byte[bufferCapacity];

        int bytesReadFirst;
        int bytesReadSecond;
        do
        {
            bytesReadFirst = firstStream.Read(firstBuffer, 0, firstBuffer.Length);
            bytesReadSecond = secondStream.Read(secondBuffer, 0, secondBuffer.Length);

            if (bytesReadFirst != bytesReadSecond || !CompareByteArrays(firstBuffer, secondBuffer))
                return false;

        } while (bytesReadFirst > 0 && bytesReadSecond > 0);

        return true;
    }

    static bool CompareByteArrays(byte[] first, byte[] second)
        => first.Length == second.Length && Interop.memcmp(first, second, first.Length) == 0;

我不明白的部分是,当已经有一个语句检查 if 和那些 int 是 读取的字节数时,为什么要检查字节数组长度。CompareByteArraysifbytesReadFirst != bytesReadSecondBufferedStream.Read()

我是否遗漏了什么,或者可以省略整个内容?first.Length == second.Length

C# 相等 字节缓冲区 比较运算符

评论

1赞 Klaus Gütter 2/25/2023
在读取的字节数小于缓冲区大小的情况下,此代码似乎是错误的,因为它将超出实际读取的数据。
0赞 Yarin_007 2/25/2023
此外,如果目标是检查两个流是否相同,则首先测试它们的长度是否相同会更快。
0赞 notarobot 2/25/2023
@KlausGütter,对不起,发布了整个方法,您仍然认为问题仍然存在吗?
0赞 notarobot 2/25/2023
@Yarin_007,发布整个方法,有这样的检查,是的。

答:

1赞 Marc Gravell 2/25/2023 #1

坦率地说,该代码完全是错误的。这是两个不同的测试:

  1. 第一个测试检查每次调用中读取的量是否相同; 没有定义它将返回的内容 - 给定一个带有 和 的缓冲区,并且可以返回ReadReadReadoffsetcount
  • 0,对于 EOF 方案
  • 如果某些数据可用,则为 1 和(含)之间的任何数字(不需要填充字节;任何方便都可以)countcount
  • 或抛出异常
  1. 第二个测试检查数组的大小是否相同;这是一个非常不同的问题

然而,由于 ,测试本身是注定要失败的;我们这里有一个巨大的缓冲区(太大了,IMO);返回 3 和返回 42 是完全有效的,这绝对不会告诉您有效负载是否相同 - 您必须继续读取两个流并比较两个流中可用的交叉字节数。最简单的方法是每次都填充两个缓冲区,即1firstStream.ReadsecondStream.Read

static ReadOnlySpan<byte> ReadAsMuchAsPossible(Stream stream, byte[] buffer)
{
    int read, totalRead = 0;
    while (totalRead < buffer.Length && (read = stream.Read(buffer, totalRead, buffer.Length - totalRead)) > 0)
    {
        totalRead += read;
    }
    return new ReadOnlySpan<byte>(buffer, 0, totalRead);
}

// ... loop over the streams, with, per iteration
Debug.Assert(firstBuffer.Length == secondBuffer.Length);
var firstChunk = ReadAsMuchAsPossible(firstStream, firstBuffer);
var secondChunk = ReadAsMuchAsPossible(secondStream, secondBuffer);
bool chunkSame = firstChunk.SequenceEqual(secondChunk);

让运行时担心优化跨度比较

评论

0赞 notarobot 2/26/2023
等。。。我刚刚意识到比较数组大小。那么,这不会总是正确的,因为两个数组都是以相同的大小创建的?返回不同的数字怎么不能告诉我任何事情?我正在测试文件是否相同,逐字节。因此,如果返回两个不同的数字,则一个字节流较短,因此不相等。此外,呼叫已经推进了导引头,或者无论它被调用什么,第二次调用它,偏移量为 0 将从第一次调用中断的地方开始读取。first.Length == second.LengthbufferCapacityReadReadRead