比较memorystream和文件C#.NET的最有效方法

比较memorystream和文件C#.NET的最有效方法,c#,.net,image,file,comparison,C#,.net,Image,File,Comparison,我有一个MemoryStream,其中包含PNG编码图像的字节,我想检查磁盘上的目录中是否有该图像数据的精确副本。第一个明显的步骤是只查找与确切长度匹配的文件,但在此之后,我想知道将内存与文件进行比较的最有效方法是什么。我在处理streams方面不是很有经验 我对这件事有一些想法: 首先,如果我能得到文件的哈希代码,比较哈希代码(大概)比比较图像的每个字节更有效。类似地,我可以比较图像的一些字节,给出一个“足够接近”的答案 当然,我可以比较整个流程,但我不知道这会有多快 将MemoryStrea

我有一个MemoryStream,其中包含PNG编码图像的字节,我想检查磁盘上的目录中是否有该图像数据的精确副本。第一个明显的步骤是只查找与确切长度匹配的文件,但在此之后,我想知道将内存与文件进行比较的最有效方法是什么。我在处理streams方面不是很有经验

我对这件事有一些想法:

首先,如果我能得到文件的哈希代码,比较哈希代码(大概)比比较图像的每个字节更有效。类似地,我可以比较图像的一些字节,给出一个“足够接近”的答案

当然,我可以比较整个流程,但我不知道这会有多快


将MemoryStream与文件进行比较的最佳方法是什么?在for循环中逐字节?

首先,获取两个流的hashcode没有帮助-要计算hashcode,您需要读取整个内容,并在读取时执行一些简单的计算。如果逐字节比较文件或使用缓冲区,则可以提前停止(在找到前两个不匹配的字节/块后)

但是,如果您需要将
MemoryStream
与多个文件进行比较,那么这种方法是有意义的,因为这样您只需要在
MemoryStream
中循环一次(计算hashcode),然后循环所有文件

在任何情况下,您都必须编写代码来读取整个文件。正如您所提到的,这可以通过逐字节或使用缓冲区来完成。将数据读入缓冲区是一个好主意,因为从HDD读取数据(例如,读取1kB缓冲区)时,这可能是更有效的操作。此外,如果需要并行处理多个文件,可以使用异步
BeginRead
方法

摘要

  • 如果需要比较多个文件,请使用hashcode
  • 要读取/比较单个文件的内容,请执行以下操作:
    • 从两个流将1kB的数据读入缓冲区
    • 查看是否有差异(如果有,退出)
    • 继续循环
如果需要并行处理多个文件,请使用
BeginRead
异步执行上述步骤。

另一种解决方案:

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);
}

使用
Stream
我们不会得到结果,每个文件都有一个唯一的标识,比如上次修改的日期等等。所以每个文件都是不同的。此信息包含在流中

我们在NeoSmart Technologies开源处理此问题,因为我们不得不多次比较不透明的
对象的字节相等性。它在NuGet上以
StreamCompare
的形式提供,您可以阅读它相对于现有方法的优势

用法非常简单:

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

var scompare = new StreamCompare();
var areEqual = await scompare.AreEqualAsync(stream1, stream2);
它的编写目的是尽可能多地消除陷阱和性能缺陷,并包含许多优化以加快比较(并最大限度地减少内存使用)。包中还包含一个文件比较包装器
FileCompare
,可用于按路径比较两个文件

StreamCompare
根据麻省理工学院许可证发布,运行在.NET标准1.3及更高版本上。可提供适用于.NET标准1.3、.NET标准2.0、.NET核心2.2和.NET核心3.0的NuGet软件包。完整文档位于
自述文件中


首先,获取这两个流的hashcode不会有帮助——要计算hashcode,您需要读取全部内容,并在读取时执行一些简单的计算

我不确定我是否误解了它,或者这根本不是真的。下面是使用流进行哈希计算的示例

私有静态字节[]计算哈希(流数据)
{
使用HashAlgorithm=MD5.Create();
字节[]字节=算法.ComputeHash(数据);
data.Seek(0,SeekOrigin.Begin);//我将使用此技巧,以便调用者不会在流处于意外位置时结束
返回字节;
}
我用benchmark.net测量了这段代码,它在900Mb的文件中分配了384字节。在这种情况下,不用说在内存中加载整个文件的效率有多低

然而,这是事实


意识到散列冲突(不太可能)的可能性很重要。为了避免这个问题,字节比较是必要的

因此,如果散列不匹配,您必须执行额外的检查,以确保文件100%不同。在这种情况下,以下是一个很好的方法

正如您所提到的,这可以通过逐字节或使用缓冲区来完成。将数据读入缓冲区是一个好主意,因为从HDD读取数据(例如,读取1kB缓冲区)时,这可能是更有效的操作

最近我不得不执行这样的检查,所以我将把这个练习的结果作为两个实用方法发布

private bool AreStreamsEqual(流、流、其他流)
{
const int bufferSize=2048;
if(other.Length!=stream.Length)
{
返回false;
}
字节[]缓冲区=新字节[bufferSize];
byte[]otherBuffer=新字节[bufferSize];
而((=stream.Read(buffer,0,buffer.Length))>0)
{
var=other.Read(otherBuffer,0,otherBuffer.Length);
如果(!otherBuffer.SequenceEqual(缓冲区))
{
stream.Seek(0,SeekOrigin.Begin);
其他.Seek(0,SeekOrigin.Begin);
返回false;
}
}
stream.Seek(0,SeekOrigin.Begin);
其他.Seek(0,SeekOrigin.Begin);
返回true;
}
私有布尔IsStreamEuqalToByteArray(字节[]内容,流)
{
const int bufferSize=2048;
var i=0;
if(contents.Length!=stream.Length)
{
返回false;
}
字节[]缓冲区=新字节[bufferSize];
int字节读取;
而((bytesRead=stream.Read(buffer,0,buffer.Length))>0)
{