在 F# 中使用 MD5
Using MD5 in F#
我目前正在尝试学习 F#。这是我第一次使用 .NET 语言,因此我对可用的 API 非常陌生。
作为初学者项目,我想实现自己的重复文件查找器。我被建议使用校验和,因为我比较的文件非常大(大部分在 1MB 到 10MB 之间)。
到目前为止,这就是我所做的:检查文件长度后,我通过将所有字节读入字节数组来比较具有相同文件长度的文件。现在我想用MD5计算每个字节数组的哈希值,然后删除具有相同哈希值的重复文件。
我有一些问题:
- MD5 是否适合此任务?
- 如果不是,我应该改用什么算法?
感谢您的帮助。我可能 post 对你的回复提出后续问题。
编辑:
let readAllBytesMD5 (tupleOfFileLengthsAndFiles) =
let md5 = MD5.Create()
tupleOfFileLengthsAndFiles
|> snd
|> Seq.map (fun eachFile -> (File.ReadAllBytes eachFile, eachFile))
|> Seq.groupBy fst
|> Seq.map (fun (byteArray, eachFile) -> (md5.ComputeHash(byteArray), eachFile))
我想提取具有多个值(对应文件)的键(散列字节数组),并删除重复文件。我如何改进和继续上面的代码示例?我不熟悉 MD5 的工作原理,所以我被困在这里。如有任何建议,我们将不胜感激。
使用 MD5 执行此任务并没有什么特别的错误。但是,MD5 不再被视为 "strong" 散列;确定的各方创建具有不同内容但具有相同 MD5 哈希值的文件相对简单。
一个更强大的替代方案,我会推荐的是您使用 SHA-2 哈希之一,如 SHA256。
但是,性能说明:如果缓存文件的哈希值(并随着文件 added/deleted/modified 增量更新缓存),哈希只会提高工具的性能。如果不缓存hash,每次发现冲突时,需要读取和两个文件的全部内容,计算它们的hash;如果此工具仅用于偶然的重复数据删除,则可能 faster/simpler 每当找到相同大小的文件时比较文件的内容。
编辑:这是您可以使用的一些示例代码。它会检测重复项,但您需要编写另一个函数来确定如何解决冲突(例如,您可能希望保留最早创建的文件)。
open System.IO
open System.Security.Cryptography
/// Given a sequence of filenames, looks for duplicate files by comparing file lengths
/// and, if necessary, hash values calculated using the specified hash algorithm.
/// Returns a sequence of tuples; the first item in the tuple is a hash value and the
/// second item is a sequence containing the names of two or more files which have
/// the same length and hash value.
let findDuplicateFiles (algorithm : HashAlgorithm) (filenames : seq<string>) =
filenames
|> Seq.groupBy (fun filename ->
(FileInfo filename).Length)
|> Seq.collect (fun (_, sameLengthFilenames) ->
// If there's only one file with this length, there's no duplication so don't return it.
if Seq.length sameLengthFilenames = 1 then Seq.empty
else
// Possible duplication. Resolve by hashing the files and comparing the hashes.
sameLengthFilenames
|> Seq.groupBy (fun filename ->
using (File.OpenRead filename) algorithm.ComputeHash)
// Check for multiple files with the same hash value.
// Return any such filenames so outside code can determine how to handle them.
|> Seq.filter (fun (_, sameLengthFilenames) ->
// Collision when two or more files have the same hash.
Seq.length sameLengthFilenames > 2))
/// Given a sequence of filenames, looks for duplicate files by comparing file lengths
/// and, if necessary, hash values calculated using the SHA256 algorithm.
/// Returns a sequence of tuples; the first item in the tuple is a hash value and the
/// second item is a sequence containing the names of two or more files which have
/// the same length and hash value.
let findDuplicateFilesSHA256 filenames =
// NOTE: The algorithm should be bound with 'use' or 'using' here so it can be disposed,
// but the F# 3.1 compiler appears to dispose the object too early.
findDuplicateFiles (SHA256.Create()) filenames
//
let printDuplicateEntry (hash : byte[], filenames : seq<string>) =
stdout.WriteLine ""
stdout.Write "Hash: "
stdout.WriteLine (System.BitConverter.ToString(hash).Replace("-", ""))
for filename in filenames do
printfn " %s (Length: %i)" filename ((FileInfo filename).Length)
//
let findDuplicateFilesInDirectory path =
Directory.EnumerateFiles (path)
|> findDuplicateFilesSHA256
|> Seq.iter printDuplicateEntry
;;
// Example usage:
findDuplicateFilesInDirectory @"C:\Users\Jack\Desktop";;
我目前正在尝试学习 F#。这是我第一次使用 .NET 语言,因此我对可用的 API 非常陌生。
作为初学者项目,我想实现自己的重复文件查找器。我被建议使用校验和,因为我比较的文件非常大(大部分在 1MB 到 10MB 之间)。
到目前为止,这就是我所做的:检查文件长度后,我通过将所有字节读入字节数组来比较具有相同文件长度的文件。现在我想用MD5计算每个字节数组的哈希值,然后删除具有相同哈希值的重复文件。
我有一些问题:
- MD5 是否适合此任务?
- 如果不是,我应该改用什么算法?
感谢您的帮助。我可能 post 对你的回复提出后续问题。
编辑:
let readAllBytesMD5 (tupleOfFileLengthsAndFiles) =
let md5 = MD5.Create()
tupleOfFileLengthsAndFiles
|> snd
|> Seq.map (fun eachFile -> (File.ReadAllBytes eachFile, eachFile))
|> Seq.groupBy fst
|> Seq.map (fun (byteArray, eachFile) -> (md5.ComputeHash(byteArray), eachFile))
我想提取具有多个值(对应文件)的键(散列字节数组),并删除重复文件。我如何改进和继续上面的代码示例?我不熟悉 MD5 的工作原理,所以我被困在这里。如有任何建议,我们将不胜感激。
使用 MD5 执行此任务并没有什么特别的错误。但是,MD5 不再被视为 "strong" 散列;确定的各方创建具有不同内容但具有相同 MD5 哈希值的文件相对简单。
一个更强大的替代方案,我会推荐的是您使用 SHA-2 哈希之一,如 SHA256。
但是,性能说明:如果缓存文件的哈希值(并随着文件 added/deleted/modified 增量更新缓存),哈希只会提高工具的性能。如果不缓存hash,每次发现冲突时,需要读取和两个文件的全部内容,计算它们的hash;如果此工具仅用于偶然的重复数据删除,则可能 faster/simpler 每当找到相同大小的文件时比较文件的内容。
编辑:这是您可以使用的一些示例代码。它会检测重复项,但您需要编写另一个函数来确定如何解决冲突(例如,您可能希望保留最早创建的文件)。
open System.IO
open System.Security.Cryptography
/// Given a sequence of filenames, looks for duplicate files by comparing file lengths
/// and, if necessary, hash values calculated using the specified hash algorithm.
/// Returns a sequence of tuples; the first item in the tuple is a hash value and the
/// second item is a sequence containing the names of two or more files which have
/// the same length and hash value.
let findDuplicateFiles (algorithm : HashAlgorithm) (filenames : seq<string>) =
filenames
|> Seq.groupBy (fun filename ->
(FileInfo filename).Length)
|> Seq.collect (fun (_, sameLengthFilenames) ->
// If there's only one file with this length, there's no duplication so don't return it.
if Seq.length sameLengthFilenames = 1 then Seq.empty
else
// Possible duplication. Resolve by hashing the files and comparing the hashes.
sameLengthFilenames
|> Seq.groupBy (fun filename ->
using (File.OpenRead filename) algorithm.ComputeHash)
// Check for multiple files with the same hash value.
// Return any such filenames so outside code can determine how to handle them.
|> Seq.filter (fun (_, sameLengthFilenames) ->
// Collision when two or more files have the same hash.
Seq.length sameLengthFilenames > 2))
/// Given a sequence of filenames, looks for duplicate files by comparing file lengths
/// and, if necessary, hash values calculated using the SHA256 algorithm.
/// Returns a sequence of tuples; the first item in the tuple is a hash value and the
/// second item is a sequence containing the names of two or more files which have
/// the same length and hash value.
let findDuplicateFilesSHA256 filenames =
// NOTE: The algorithm should be bound with 'use' or 'using' here so it can be disposed,
// but the F# 3.1 compiler appears to dispose the object too early.
findDuplicateFiles (SHA256.Create()) filenames
//
let printDuplicateEntry (hash : byte[], filenames : seq<string>) =
stdout.WriteLine ""
stdout.Write "Hash: "
stdout.WriteLine (System.BitConverter.ToString(hash).Replace("-", ""))
for filename in filenames do
printfn " %s (Length: %i)" filename ((FileInfo filename).Length)
//
let findDuplicateFilesInDirectory path =
Directory.EnumerateFiles (path)
|> findDuplicateFilesSHA256
|> Seq.iter printDuplicateEntry
;;
// Example usage:
findDuplicateFilesInDirectory @"C:\Users\Jack\Desktop";;