VB.Net 中的文件比较

File comparison in VB.Net

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

基本上我正在做的是将文件内容流式传输到字节数组,并通过 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

我会说对文件进行哈希处理是可行的方法,我过去就是这样做的。

在使用 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

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

第一个推荐是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。特别是如果您采用第一个建议,只需使用如果文件不存在则失败的选项打开方法顶部附近的流,并检查流是否打开。