是否有必要读取每个字节以检查复制的文件是否与原始文件相同?


16

我最近了解到一个名为Total Commander的程序。它是Windows资源管理器的替代品,具有自己的用于复制文件的内容。为了检查文件是否相同,它不计算CRC,而是逐字检查原始文件和副本上的每个字节。

我的问题是:这必要吗?CRC或任何其他此类技术会出错吗?作为程序员,您是否应该尝试实施这种完善而缓慢的系统,还是过于极端?


3
看看“ rsync”是如何处理的。

21
计算两个文件中的CRC(或更佳的sha1sum)都需要读取每个字节。如果进行逐字节比较,发现不匹配就可以立即退出-您不必担心两个不同的文件恰好具有相同的校验和(尽管对于sha1sum来说消失的可能性很小) 。另一方面,当您比较不在同一台计算机上的文件时,校验和比较很有用。校验和可以在本地计算,您不必通过网络传输全部内容。
基思·汤普森

3
至于冲突的可能性,如果您像这样使用像样的哈希,则sha1sum几乎不必担心它,除非有人故意和昂贵地构造sha1sum冲突的文件。我没有消息来源,但我听说(在git的背景下)两个不同文件具有相同sha1sum的可能性与开发团队的每个成员被吃掉的可能性几乎相同狼。在同一天。在完全无关的事件中。
基思·汤普森

5
@KeithThompson:我想您的第一个评论应该是一个答案:-)
Dean Harding

6
简短答案-不,最好只是让您的计算机为您完成。
psr 2012年

Answers:


40

计算两个文件中的CRC(或更佳的sha1sum)都需要读取每个字节。如果进行逐字节比较,发现不匹配就可以立即退出-您不必担心两个不同的文件恰好具有相同的校验和(尽管对于sha1sum来说消失的可能性很小) 。因此,如果您在本地进行比较,则逐字节比较至少会和校验和比较一样快(除非您已经计算了校验和)。

另一方面,当您比较不在同一台计算机上的文件时,校验和比较很有用。校验和可以在本地计算,您不必通过网络传输全部内容。

混合方法也是可能的。例如,您可以一次计算并比较两个文件的校验和,这可以避免读取整个文件(如果它们不同),同时还可以避免通过网络传输整个文件。在rsync的协议做这样的事情。

请注意,如Dave Rager在其回答中所述,使用简单的CRC可以给您带来很大的碰撞机会。至少使用sha1sum,甚至更新的东西。(不要尝试发明自己的哈希算法;开发sha1sum的人比我们任何人都对这种东西了解得多。)

至于冲突的可能性,如果您使用像sha1sum这样的体面的哈希,则几乎不必担心它,除非有人故意和昂贵地构造其sha1sums发生冲突的文件(当我第一次编写此文件时,生成此类冲突是不可行的) ,但正在取得进展)。引用Scott Chacon的“ Pro Git”第6.1节

这是一个示例,可让您大致了解发生SHA-1冲突的情况。如果地球上所有的65亿人都在编程,并且每一秒钟,每个人所产生的代码都相当于整个Linux内核历史记录(100万个Git对象)并将其推入一个巨大的Git存储库,则需要5年的时间该存储库包含足够的对象,以使发生一次SHA-1对象冲突的可能性为50%。更有可能的是,您的编程团队的每个成员都将在同一晚被无关事件中的狼袭击并杀死。

总结:

逐字节比较适用于本地比较。sha1sum可用于远程比较,并且不会有很大的误报率。


应当指出的是,一个“好”的散列函数的一般定义,包括它的性质非常努力创造相同的哈希(“碰撞性”)不同的输入。SHA-1在这方面有一些(到目前为止是理论上的)弱点,但是即使您相当努力,也不能仅仅“构造两个冲突的文件”。
sleske 2012年

@sleske:更新
Keith Thompson,

1
@KeithThompson我正在回答答案,但我认为现在该是SHA1更新的时候了-SHAppening
K.Steff

我怀疑如果您尝试在GitHub上托管此理论存储库,他们会胡思乱想。
hBy2Py

1
我的意思是说,如果他们每秒要处理多少埃字节的数据,他们将不满意。:-)
hBy2Py

10

这是另一种思考方式。

如果不可能使两个不同的文件具有相同的CRC,则扩展名意味着每个文件都可以由一个唯一的CRC表示;如果CRC小于原始文件,则表示一种无损压缩形式。如果没有,那么比较原始文件也一样,因为您将比较相同数量的字节。

从理论上讲,您可以在比较的两边使用无损压缩来减少比较中必需的字节数,但这是一个愚蠢的做法,因为您会浪费更多的周期,并且必须读取两个文件的每个字节来进行压缩。也就是说,要以无损压缩方案对每个字节(及其顺序)进行编码,您必须先将其读入并将其插入算法,对吗?游戏结束。

这是一个类比:
如果您想要一种无需两个字母就可以快速确定两个打印文档是否相同的方法,则可以比较文档每一行上的字母数。如果计数全部匹配,则文档完全相同的几率将大大提高,但是没有人会说使用这种方法可以确定每个字母都是相同的。


3

检查相同文件的唯一完美方法是字节比较。合理近似的另一种方法是为文件计算哈希(例如MD5)并将其进行比较。可能存在哈希冲突,但可能性很小。

我可以想象,在进行比较时,用于字节比较的字节比在两个文件上计算哈希值要快。但是,如果您的应用程序预先计算散列并存储有关文件的元数据,则比较散列将明显更快。

CRC可能不是走的路,因为它只是一种错误检测机制,而不是哈希。(或哈希值不高,可能会发生很多冲突)


+1同意。与具有良好哈希功能的偶然碰撞(CRC32弱-也同意)相比,硬盘损坏的可能性更大。
米哈尔Šrajer

2

为了100%地确定两个文件相同,您确实需要检查字节。

为什么?哈希冲突,这就是原因!根据散列所使用的算法,冲突可能或多或少地可能发生,但仍然有可能发生。请按照下列步骤操作:

  1. 检查文件大小
  2. 检查MIME类型
  3. 检查哈希
  4. 检查一些随机偏移并比较位

这将为您提供非常高的确定性,即两个文件相同,但是您发生碰撞的可能性很小(非常小)。您要进行比较的范围取决于情况。


我认为,如果选择良好的哈希算法,则2.和4.不会给您带来任何真正的“平等”质量。可能仅对于弱哈希也需要1.。
米哈尔Šrajer

1
-1这没有意义。如果选择一个好的哈希算法,则所有其他步骤都是多余的。1.和4.实际上已经被散列处理了,而2.已经是胡说八道了(大多数文件系统甚至都没有“ MIME类型”的概念,即使有,它添加的信息也很少)。
sleske 2012年

@sleske我的意思是您可以执行一些不太繁琐的初步操作,而不是对文件进行散列处理(这是一项繁重的操作)。

我认为只有1和3很有意义。(1)将标记大多数情况下的不同文件,以节省需要计算哈希的情况。相同长度文件上的哈希冲突不太可能,因此不值得担心。
迈克尔·肖

1

正如其他人所说,如果两个文件在同一系统上,则逐字节比较会更快。如果您要比较一堆文件,那么如果文件在旋转存储上,散列是更好的答案。

当您没有所有可用的数据时,散列真的很闪耀。例如,文件位于不同的计算机上。它还使您可以保存计算结果,并在以后引用它们。(此报告是否与旧报告相同?在制作报告时将其保存为哈希值。在制作下一份报告时,您可以简单地比较哈希值。不仅不需要阅读旧的哈希值,还可以甚至不需要提供它的副本。)


0

我认为,在检查@Glenn Nelson概述的文件属性之后,应该将提供的文件比较实用程序与操作系统一起使用,或者使用文件比较工具(请参阅:wiki-文件比较工具)来比较内容。

我认为CRC并非100%准确,而且我认为其准确性会随着文件长度的增加而降低。另外,我不建议您从头开始编写它,因为它可能需要大量测试。


0

是否有必要读取每个字节以检查复制的文件是否与原始文件相同?是的,可以100%确定

是否有必要读取每个字节以检查复制的文件是否与原始文件不同?没有

因此,要快速确定非同一性,请首先检查元数据,例如文件大小以及OS /文件系统/存储可能已经维护的任何校验和/ CRC或MIME类型。由于它们是由该系统预先计算的,因此您在比较时无需支付此费用。

如果该测试通过,则如果需要100%确定,您仍需要单独比较每个字节,但是请注意,在现代流水线CPU中,使用多个线程以及可能的多个处理器/ CPU,对大型文件进行块比较确实非常快和高效,因为该过程高度可并行化。比任何涉及每个字节的数学计算都要快(尽管某些算法也可以并行化,但可能不那么容易或如此好)。这是因为流水线化的CPU可以对微码甚至是硬件(非常快)进行内存的块比较操作,并且磁盘到内存子系统经过高度优化,可以将大量文件块与内存进行并行处理,并且可以硬件。如果您的应用程序定期执行此类操作,并且这是一个已知的性能瓶颈,则明智的做法是使用编写良好的多线程代码来实现此目的,以利用您的操作系统和硬件的并行化功能(也许使用一种旨在这个)。

仅当您要处理每个文件一次并在以后进行多次比较(您记住[“缓存”]摘要或“压缩” [如JohnFX所说的]分析结果)时,这样做才会有很大的好处,即使那样,也只是为了证明差异(可能);为了证明相同性,您仍然需要逐字节进行比较。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.