比较内存流和文件的最有效方法C# .NET

ego6inou  于 2023-11-20  发布在  .NET
关注(0)|答案(6)|浏览(149)

我有一个包含PNG编码图像字节的MemoryStream,想检查磁盘上的某个目录中是否有该图像数据的精确副本。第一个明显的步骤是只查找与精确长度匹配的文件,但在此之后,我想知道什么是最有效的方法来比较内存和文件。我对流不是很有经验。
我对这件事有几个想法:
首先,如果我能得到文件的哈希码,那么(大概)比较哈希码比图像的每个字节更有效。同样,我可以只比较图像的一些字节,给出一个“足够接近”的答案。
当然,我可以比较整个流,但我不知道有多快。
比较MemoryStream和文件的最佳方法是什么?在for循环中逐字节比较?

tez616oj

tez616oj1#

另一个解决方案是:

private static bool CompareMemoryStreams(MemoryStream ms1, MemoryStream ms2)
{
    if (ms1.Length != ms2.Length)
        return false;
    ms1.Position = 0;
    ms2.Position = 0;

    var msArray1 = ms1.ToArray();
    var msArray2 = ms2.ToArray();

    return msArray1.SequenceEqual(msArray2);
}

字符串

n53p2ov0

n53p2ov02#

首先,获取两个流的hashcode没有帮助-要计算hashcode,您需要读取整个内容 * 并在阅读时执行一些简单的计算。如果您逐字节或使用缓冲区比较文件,那么您可以提前停止(在您找到前两个字节/块之后)不匹配。
但是,如果您需要将MemoryStream与多个文件进行比较,则这种方法是有意义的,因为这样您只需要循环MemoryStream一次(以计算hashcode),然后循环所有文件。
在任何情况下,你都必须写代码来读取整个文件。正如你提到的,这可以通过逐字节或使用缓冲区来完成。阅读数据到缓冲区是一个好主意,因为当从HDD中阅读时(例如,阅读1 kB缓冲区),它可能是更有效的操作。此外,如果你需要并行处理多个文件,你可以使用异步BeginRead方法。

摘要

  • 如果需要比较多个文件,请使用hashcode
  • 读取/比较单个文件的内容:
  • 从两个数据流中读取1 kB数据到缓冲区中
  • 看看是否有区别(如果有,退出)
  • 继续循环

如果您需要并行处理多个文件,请使用BeginRead异步实现上述步骤。

b1zrtrql

b1zrtrql3#

首先,获取两个流的散列码没有帮助--要计算散列码,您需要读取整个内容,并在阅读时执行一些简单的计算。
我不确定是我误解了它,还是这根本就不是真的。

private static byte[] ComputeHash(Stream data)
{
    using HashAlgorithm algorithm = MD5.Create();
    byte[] bytes = algorithm.ComputeHash(data);
    data.Seek(0, SeekOrigin.Begin); //I'll use this trick so the caller won't end up with the stream in unexpected position
    return bytes;
}

字符集
我在benchmark.net上测试过这个代码,它在900 Mb的文件上分配了384个字节。
但这是真的
注意哈希冲突的可能性(不太可能)是很重要的。字节比较是避免这个问题的必要手段。
因此,如果哈希值不匹配,您必须执行额外的检查,以确保文件是100%不同的。在这种情况下,以下是一个很好的方法。
正如您提到的,这可以通过逐字节或使用缓冲区来完成。将数据读入缓冲区是一个好主意,因为从HDD读取数据(例如读取1 kB缓冲区)时,这可能是一个更有效的操作。
最近,我不得不执行这样的检查,所以我将发布此练习的结果作为2个实用程序方法

private bool AreStreamsEqual(Stream stream, Stream other)
{
    const int bufferSize = 2048;
    if (other.Length != stream.Length)
    {
        return false;
    }

    byte[] buffer = new byte[bufferSize];
    byte[] otherBuffer = new byte[bufferSize];
    while ((_ = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
        var _ = other.Read(otherBuffer, 0, otherBuffer.Length);

        if (!otherBuffer.SequenceEqual(buffer))
        {
            stream.Seek(0, SeekOrigin.Begin);
            other.Seek(0, SeekOrigin.Begin);
            return false;
        }
    }
    stream.Seek(0, SeekOrigin.Begin);
    other.Seek(0, SeekOrigin.Begin);
    return true;
}

private bool IsStreamEuqalToByteArray(byte[] contents, Stream stream)
{
    const int bufferSize = 2048;
    var i = 0;
    if (contents.Length != stream.Length)
    {
        return false;
    }
    
    byte[] buffer = new byte[bufferSize];
    int bytesRead;
    while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
    {
        var contentsBuffer = contents
            .Skip(i * bufferSize)
            .Take(bytesRead)
            .ToArray();

        if (!contentsBuffer.SequenceEqual(buffer))
        {
            stream.Seek(0, SeekOrigin.Begin);
            return false;
        }
    }
    stream.Seek(0, SeekOrigin.Begin);
    return true;
}

ercv8c1e

ercv8c1e4#

我们在NeoSmart Technologies开源了a library来处理这个问题,因为我们不得不比较不透明的Stream对象的字节相等性。它在NuGet上以StreamCompare的形式提供,你可以阅读它相对于现有方法in the official release announcement的优势。
用法非常简单:

var stream1 = ...;
var stream2 = ...;

var scompare = new StreamCompare();
var areEqual = await scompare.AreEqualAsync(stream1, stream2);

字符集
它尽可能地消除了许多陷阱和性能陷阱,并包含了许多优化来加速比较(并最大限度地减少内存使用)。包中还包含一个文件比较 Package 器FileCompare,可用于按路径比较两个文件。
StreamCompare在MIT许可证下发布,可在.NET Standard 1.3及更高版本上运行。适用于.NET Standard 1.3、.NET Standard 2.0、.NET Core 2.2和.NET Core 3.0的NuGet软件包可用。完整文档在README文件中。

l5tcr1uw

l5tcr1uw5#

rdfind有一个有趣的算法。除了比较大小之外,它还首先查看第一个和最后一个字节(由于标准化的文件头,第一个字节通常是相同的)。参见rdfind

smtd7mpg

smtd7mpg6#

使用Stream我们不会得到结果,每个文件都有一个唯一的标识,例如最后修改日期等。因此每个文件都是不同的。这些信息包含在流中

相关问题