【问题标题】:File comparison in VB.NetVB.Net中的文件比较
【发布时间】:2015-03-03 14:30:47
【问题描述】:

我需要知道两个文件是否相同。起初我比较了文件大小和创建时间戳,但这还不够可靠。我想出了以下代码,这似乎可行,但我希望有人有更好、更简单或更快的方法。

基本上我正在做的是将文件内容流式传输到字节数组,并通过 System.Security.Cryptography 比较他们的 MD5 哈希。

在此之前我会进行一些简单的检查,因为如果两个文件路径相同或其中一个文件不存在,则没有理由通读文件。

Public Function CompareFiles(ByVal file1FullPath As String, ByVal file2FullPath As String) As Boolean

    If Not File.Exists(file1FullPath) Or Not File.Exists(file2FullPath) Then
        'One or both of the files does not exist.
        Return False
    End If

    If String.Compare(file1FullPath, file2FullPath, True) = 0 Then
        ' fileFullPath1 and fileFullPath2 points to the same file...
        Return True
    End If

    Dim MD5Crypto As New MD5CryptoServiceProvider()
    Dim textEncoding As New System.Text.ASCIIEncoding()

    Dim fileBytes1() As Byte, fileBytes2() As Byte
    Dim fileContents1, fileContents2 As String
    Dim streamReader As StreamReader = Nothing
    Dim fileStream As FileStream = Nothing
    Dim isIdentical As Boolean = False

    Try

        ' Read file 1 to byte array.
        fileStream = New FileStream(file1FullPath, FileMode.Open)
        streamReader = New StreamReader(fileStream)
        fileBytes1 = textEncoding.GetBytes(streamReader.ReadToEnd)
        fileContents1 = textEncoding.GetString(MD5Crypto.ComputeHash(fileBytes1))
        streamReader.Close()
        fileStream.Close()

        ' Read file 2 to byte array.
        fileStream = New FileStream(file2FullPath, FileMode.Open)
        streamReader = New StreamReader(fileStream)
        fileBytes2 = textEncoding.GetBytes(streamReader.ReadToEnd)
        fileContents2 = textEncoding.GetString(MD5Crypto.ComputeHash(fileBytes2))
        streamReader.Close()
        fileStream.Close()

        ' Compare byte array and return result.
        isIdentical = fileContents1 = fileContents2

    Catch ex As Exception

        isIdentical = False

    Finally

        If Not streamReader Is Nothing Then streamReader.Close()
        If Not fileStream Is Nothing Then fileStream.Close()
        fileBytes1 = Nothing
        fileBytes2 = Nothing

    End Try

    Return isIdentical
End Function

【问题讨论】:

  • 在问题中发布任何非html/javascript/css代码时,请勿使用带有<>符号的页面图标,请使用{}按钮。
  • 这似乎是一种很好的做事方式。我会将您的方法拆分为具有特定功能的较小方法,例如 GetFileMD5()。您是在寻求意见还是有具体问题?
  • 现在我已经实际查看了代码,您可以稍微缩短代码,但使用 StreamReader 对象上的 Using 语句。此外,如果您只是想根据数据内容了解文件是否重复,您可以跳过文本编码并仅比较流的 MD5。另一件事,StreamReaders 通常用于读取文本文件,如果这些文件有可能是其他文件类型,您应该考虑使用 FileStream 对象
  • 抱歉用 而不是 {},菜鸟错误 :-) 但是感谢 cmets,非常有帮助!

标签: vb.net comparison filestream


【解决方案1】:

我会说散列文件是要走的路,这就是我过去的做法。

在处理 Streams 等时使用 Using 语句,因为它们会自行清理。 这是一个例子。

Public Function CompareFiles(ByVal file1FullPath As String, ByVal file2FullPath As String) As Boolean

If Not File.Exists(file1FullPath) Or Not File.Exists(file2FullPath) Then
    'One or both of the files does not exist.
    Return False
End If

If file1FullPath = file2FullPath Then
    ' fileFullPath1 and fileFullPath2 points to the same file...
    Return True
End If

Try
    Dim file1Hash as String = hashFile(file1FullPath)
    Dim file2Hash as String = hashFile(file2FullPath)

    If file1Hash = file2Hash Then
        Return True
    Else
        Return False
    End If

Catch ex As Exception
    Return False
End Try
End Function

Private Function hashFile(ByVal filepath As String) As String
    Using reader As New System.IO.FileStream(filepath, IO.FileMode.Open, IO.FileAccess.Read)
        Using md5 As New System.Security.Cryptography.MD5CryptoServiceProvider
            Dim hash() As Byte = md5.ComputeHash(reader) 
            Return System.Text.Encoding.Unicode.GetString(hash) 
        End Using
    End Using
End Function

【讨论】:

  • 我喜欢你将散列移动到一个单独的函数中,因为它已经完成了两次。很好地使用“使用”进行清理。我不知道您可以直接在 ComputeHash 方法中使用 fileStream,这是一个很好的改进!
  • hashToString 不见了?我猜它只是将字节数组转换为字符串,还是我忽略了什么?
  • 很好,很抱歉。代码来自一个旧项目。是的 hashtoString 是一个函数,它只是将字节数组转换为字符串。我已经更新了我的答案。
  • 注意 如果您查找 MD5,您会发现:“可以使用较新的哈希函数,例如安全哈希算法 SHA-256 和 SHA-512。考虑使用SHA256 类或 SHA512 类而不是 MD5 类。使用 MD5 仅用于与旧应用程序和数据的兼容性。这是一个优雅的例程;也许你应该更新它。
【解决方案2】:

这就是 md5 的用途。你做对了。但是,如果您真的想进一步改进它,我可以向explore推荐一些东西。重点是探索,因为这些都不是灌篮。他们可能会有所帮助,但也可能会造成伤害,或者他们可能会矫枉过正。您需要根据您的情况评估它们并(通过测试)确定最佳解决方案。

第一个推荐是compute the md5 hash without loading the entire file into RAM。该示例是 C#,但 VB.Net 翻译相当简单。如果您正在处理小文件,那么您已经拥有的可能没问题。但是,对于任何大到足以在 .Net 的大型对象堆(85,000 字节)上结束的东西,您可能需要考虑改用流技术。

此外,如果您使用的是 .Net 的最新版本,您可能希望探索为每个文件异步执行此操作。实际上,我怀疑您会从现有设备中获得最佳性能,因为磁盘 I/O 可能是其中最慢的部分,如果您允许传统磁盘读取,我希望传统磁盘性能最佳按顺序从文件中查找,而不是让您的磁盘在文件之间来回查找。但是,您仍然可以使用异步方法做得更好,特别是如果您遵循前面的建议,因为除了等待整个文件之外,您还可以在 Read() 调用级别等待。此外,如果您在 SSD 上运行它,这将最大限度地减少搜索问题,并使异步解决方案成为明显的赢家。不过有一个警告:这是一个值得追逐的深兔子洞……这可能是值得的,但您最终也可能会在YAGNI 情况上花费大量时间。但是,您可能会选择探索一次,以应对您可能不会使用它的情况,以便您对它有足够的了解,以了解它如何在您确实需要的情况下提供帮助它。

还有一点是,要使异步建议起作用,您需要将散列代码隔离到它自己的方法中……但无论如何您都应该这样做。

我的最终建议是删除File.Exists() 检查。这是一个诱人的测试,我知道,但是it's almost always wrong。特别是如果您采用第一个建议,只需使用如果文件不存在则失败的选项打开方法顶部附近的流,并检查流是否打开。

【讨论】:

  • 我已经写了这个答案的大部分内容,并在我完成之前被叫走了。当我回来时,这里的另一个答案已经涵盖了大部分要点。我不想失去工作,所以我还是发布了它。此外,我关于 asynch 和 File.Exists() 的 cmets 仍然有效。也就是说,这里的另一个答案已经突出了最重要的点:它使用流来创建哈希,而不是读取文件,我完全赞同它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多