Quel est le moyen le plus rapide de créer une fonction de hachage qui sera utilisée pour vérifier si deux fichiers sont égaux?
La sécurité n'est pas très importante.
Edit: j'envoie un fichier via une connexion réseau et je suis sûr que les deux fichiers sont identiques
Une approche pourrait consister à utiliser un simple algorithme CRC-32, et uniquement si les valeurs du CRC se comparent, relancez le hachage avec un SHA1 ou un système plus robuste. Un CRC-32 rapide surperformera chaque jour un hachage cryptographiquement sécurisé.
Sauf si vous utilisez un hachage très compliqué et/ou lent, le chargement des données à partir du disque prendra beaucoup plus de temps que le calcul du hachage (sauf si vous utilisez des disques RAM ou des disques SSD haut de gamme).
Donc, pour comparer deux fichiers, utilisez cet algorithme:
Cela permet un échec rapide (si les tailles sont différentes, vous savez que les fichiers sont différents).
Pour rendre les choses encore plus rapides, vous pouvez calculer le hachage une fois et le sauvegarder avec le fichier. Enregistrez également la date et la taille du fichier dans ce fichier supplémentaire afin que vous sachiez rapidement quand vous devez recalculer le hachage ou supprimer le fichier de hachage lorsque le fichier principal est modifié.
xxhash se prétend assez rapide et fort, en termes de collision:
http://cyan4973.github.io/xxHash/
Il existe une variante 64 bits qui fonctionne "encore plus vite" sur les processeurs 64 bits que le 32, dans l’ensemble, bien que plus lent sur les processeurs 32 bits (voir figure).
http://code.google.com/p/crcutil est également considéré comme étant assez rapide (et exploite les instructions CRC matérielles, le cas échéant, qui sont probablement très rapides, mais si vous ne disposez pas du matériel qui les prend en charge , ne sont pas aussi rapides). Je ne sais pas si CRC32c est aussi bon d'un hash (en termes de collisions) que xxHash ou pas ...
https://code.google.com/p/cityhash/ semble similaire et associé à crcutil [en ce sens qu'il peut être compilé pour utiliser des instructions CRC32c matérielles, le cas échéant].
Si vous "ne voulez que la vitesse brute la plus rapide" et que vous vous souciez moins de la qualité de la distribution aléatoire de la sortie de hachage (par exemple, avec de petits ensembles ou lorsque la vitesse est primordiale), voici quelques algorithmes rapides mentionnés: http://www.sanmayce.com/Fastest_Hash/ (ces algorithmes du type de distribution "pas tout à fait aléatoire" sont, dans certains cas, "assez bons" et très rapides). Apparemment, FNV1A_Jesteress
est le plus rapide pour les chaînes "longues", certaines autres éventuellement pour les petites chaînes. http://locklessinc.com/articles/fast_hash/ semble également lié. Je n'ai pas cherché à savoir quelles étaient les propriétés de collision de celles-ci.
Vous pouvez essayer MurmurHash , qui a été spécialement conçu pour être rapide et assez simple à coder. Vous voudrez peut-être faire un deuxième hachage plus sûr si MurmurHash renvoie un match, juste pour être sûr.
Pour ce type d’application, Adler32 est probablement l’algorithme le plus rapide, avec un niveau de sécurité raisonnable. Pour les fichiers plus volumineux, vous pouvez calculer plusieurs valeurs de hachage, par exemple une par bloc de 5 Mo du fichier, ce qui diminue les risques d'erreur (c'est-à-dire des cas où les hachages sont identiques alors que le contenu du fichier diffère). De plus, cette configuration de valeurs de hachage multiple peut permettre au calcul du hachage d'être implémenté de manière multi-thread.
Edit: (suite à la remarque de Steven Sudit)
Un mot d'avertissement si les fichiers sont petits!
Les propriétés "cryptographiques" d'Adler32, ou plutôt ses faiblesses, sont bien connues, notamment pour les messages courts. Pour cette raison, la solution proposée doit être évitée pour les fichiers de moins de quelques kilo-octets.
Néanmoins, dans la question, le PO recherche explicitement un algorithme rapide} et renonce aux préoccupations concernant la sécurité. De plus, la recherche de la vitesse peut impliquer de manière plausible que on a affaire à de "gros" fichiers plutôt qu'à de petits fichiers. Dans ce contexte, Adler32, éventuellement appliqué en parallèle pour des morceaux de fichiers de 5 Mo par exemple, reste une réponse très valable. Alder32 est réputé pour sa simplicité et sa rapidité. En outre, sa fiabilité, tout en restant inférieure à celle de CRC de même longueur, est tout à fait acceptable pour les messages de plus de 4000 octets.
S'il ne s'agit que d'une tâche ponctuelle, sachant que vous devrez lire les deux fichiers pour générer un hachage des deux, pourquoi ne pas simplement lire une petite quantité de chaque fichier à la fois et comparer?
À défaut, CRC est un algorithme très simple.
Ce que nous optimisons ici, c’est le temps consacré à une tâche ..__ Malheureusement, nous ne connaissons pas suffisamment la tâche en cours pour savoir quelle solution optimale devrait être.
S'agit-il d'une comparaison ponctuelle de 2 fichiers arbitraires? Ensuite, comparez la taille, puis comparez simplement les fichiers, octet par octet (ou mb par mb) si cela convient mieux à votre IO.
S'il s'agit de 2 grands ensembles de fichiers, ou de plusieurs ensembles de fichiers, et que ce n'est pas un exercice ponctuel. mais quelque chose qui se produira fréquemment, il faut alors stocker des hachages pour chaque fichier. Un hachage n’est jamais unique, mais un hachage comportant un nombre de 9 chiffres (32 bits) serait bon pour environ 4 milliards de combinaisons et un nombre de 64 bits serait suffisant pour distinguer entre 16 * 10 ^ 18 fichiers différents. .
Un bon compromis consisterait à générer 2 hachages 32 bits pour chaque fichier, un pour les premiers 8k, un autre pour 1 Mo + 8k, en les claquant comme un seul numéro 64 bits. Le catalogage de tous les fichiers existants dans une base de données doit être assez rapide et la recherche d'un fichier candidat par rapport à cette base de données doit également être très rapide. Une fois la correspondance établie, le seul moyen de déterminer si elles sont identiques est de comparer tous les fichiers.
Je suis convaincu de donner aux gens ce dont ils ont besoin, ce qui n’est pas toujours ce dont ils pensent avoir besoin, ni ce qu’ils veulent.
Pourquoi voulez-vous le hacher?
Si vous voulez vous assurer que deux fichiers sont égaux, alors, par définition, vous devrez lire le fichier en entier (à moins qu'ils ne soient littéralement le même fichier, auquel cas vous pouvez le savoir en consultant les métadonnées du système de fichiers). Quoi qu'il en soit, aucune raison de hachage, il suffit de les lire et de voir s'ils sont identiques. Le hachage le rendra moins efficace. Et même si les hachages concordent, vous ne savez toujours pas si les fichiers sont vraiment égaux.
Edit: Cette réponse a été postée avant que la question ne spécifie quoi que ce soit sur un réseau. Il a juste demandé de comparer deux fichiers. Maintenant que je sais qu’il ya un saut de réseau entre les fichiers, je dirais qu’il suffit d’utiliser un hachage MD5 et d’en finir.
Voici le code pour rechercher les fichiers en double de mon projet personnel afin de trier les images, ce qui supprime également les doublons. D'après mon expérience, utiliser d'abord l'algorithme de hachage rapide comme CRC32, puis MD5 ou SHA1 était encore plus lent et n'apportait aucune amélioration, car la plupart des fichiers de même taille étaient dupliqués. , cette approche peut ne pas être correcte pour tous les types de projets mais elle est certainement vraie pour les fichiers image. Ici, je fais du hachage MD5 ou SHA1 uniquement sur les fichiers de même taille.
PS: Cela dépend du codec Apache commons pour générer du hachage efficacement.
Exemple d'utilisation: new DuplicateFileFinder ("MD5"). FindDuplicateFilesList (filesList);
import Java.io.File;
import Java.io.FileInputStream;
import Java.io.IOException;
import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.HashMap;
import Java.util.Iterator;
import Java.util.List;
import Java.util.Map;
import org.Apache.commons.codec.digest.DigestUtils;
/**
* Finds the duplicate files using md5/sha1 hashing, which is used only for the sizes which are of same size.
*
* @author HemantSingh
*
*/
public class DuplicateFileFinder {
private HashProvider hashProvider;
// Used only for logging purpose.
private String hashingAlgo;
public DuplicateFileFinder(String hashingAlgo) {
this.hashingAlgo = hashingAlgo;
if ("SHA1".equalsIgnoreCase(hashingAlgo)) {
hashProvider = new Sha1HashProvider();
} else if ("MD5".equalsIgnoreCase(hashingAlgo)) {
hashProvider = new Md5HashProvider();
} else {
throw new RuntimeException("Unsupported hashing algorithm:" + hashingAlgo + " Please use either SHA1 or MD5.");
}
}
/**
* This API returns the list of duplicate files reference.
*
* @param files
* - List of all the files which we need to check for duplicates.
* @return It returns the list which contains list of duplicate files for
* e.g. if a file a.JPG have 3 copies then first element in the list
* will be list with three references of File reference.
*/
public List<List<File>> findDuplicateFilesList(List<File> files) {
// First create the map for the file size and file reference in the array list.
Map<Long, List<File>> fileSizeMap = new HashMap<Long, List<File>>();
List<Long> potDuplicateFilesSize = new ArrayList<Long>();
for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) {
File file = (File) iterator.next();
Long fileLength = new Long(file.length());
List<File> filesOfSameLength = fileSizeMap.get(fileLength);
if (filesOfSameLength == null) {
filesOfSameLength = new ArrayList<File>();
fileSizeMap.put(fileLength, filesOfSameLength);
} else {
potDuplicateFilesSize.add(fileLength);
}
filesOfSameLength.add(file);
}
// If we don't have any potential duplicates then skip further processing.
if (potDuplicateFilesSize.size() == 0) {
return null;
}
System.out.println(potDuplicateFilesSize.size() + " files will go thru " + hashingAlgo + " hash check to verify if they are duplicate.");
// Now we will scan the potential duplicate files, and eliminate false positives using md5 hash check.
List<List<File>> finalListOfDuplicates = new ArrayList<List<File>>();
for (Iterator<Long> potDuplicatesFileSizeIterator = potDuplicateFilesSize
.iterator(); potDuplicatesFileSizeIterator.hasNext();) {
Long fileSize = (Long) potDuplicatesFileSizeIterator.next();
List<File> potDupFiles = fileSizeMap.get(fileSize);
Map<String, List<File>> trueDuplicateFiles = new HashMap<String, List<File>>();
for (Iterator<File> potDuplicateFilesIterator = potDupFiles.iterator(); potDuplicateFilesIterator
.hasNext();) {
File file = (File) potDuplicateFilesIterator.next();
try {
String md5Hex = hashProvider.getHashHex(file);
List<File> listOfDuplicatesOfAFile = trueDuplicateFiles.get(md5Hex);
if (listOfDuplicatesOfAFile == null) {
listOfDuplicatesOfAFile = new ArrayList<File>();
trueDuplicateFiles.put(md5Hex, listOfDuplicatesOfAFile);
}
listOfDuplicatesOfAFile.add(file);
} catch (IOException e) {
e.printStackTrace();
}
}
Collection<List<File>> dupsOfSameSizeList = trueDuplicateFiles.values();
for (Iterator<List<File>> dupsOfSameSizeListIterator = dupsOfSameSizeList.iterator(); dupsOfSameSizeListIterator
.hasNext();) {
List<File> list = (List<File>) dupsOfSameSizeListIterator.next();
// It will be duplicate only if we have more then one copy of it.
if (list.size() > 1) {
finalListOfDuplicates.add(list);
System.out.println("Duplicate sets found: " + finalListOfDuplicates.size());
}
}
}
return finalListOfDuplicates;
}
abstract class HashProvider {
abstract String getHashHex(File file) throws IOException ;
}
class Md5HashProvider extends HashProvider {
String getHashHex(File file) throws IOException {
return DigestUtils.md5Hex(new FileInputStream(file));
}
}
class Sha1HashProvider extends HashProvider {
String getHashHex(File file) throws IOException {
return DigestUtils.sha1Hex(new FileInputStream(file));
}
}
}
Dans tous les cas, vous devez lire chaque fichier intégralement (sauf dans le cas où les tailles ne correspondent pas), il suffit donc de lire les deux fichiers et de comparer bloc à bloc.
L'utilisation du hachage ne fait que gagner en consommation de processeur et rien de plus. Comme vous n’écrivez rien, le cache du système d’exploitation supprime efficacement les données que vous lisez. Sous Linux, utilisez simplement cmp tool
Je me souviens que les anciens protocoles de transfert de modem, tels que Zmodem, permettaient une sorte de comparaison CRC pour chaque bloc envoyé. CRC32, si je me souviens assez bien de l'histoire ancienne. Je ne suggère pas que vous créiez votre propre protocole de transfert, à moins que ce ne soit exactement ce que vous faites, mais vous pourriez peut-être le faire vérifier ponctuellement un bloc du fichier, ou peut-être que le hachage de chaque bloc de 8k serait assez simple les processeurs à gérer. Je n'ai pas essayé moi-même.
vous pouvez vérifier l’algorithme utilisé par les développeurs samba/rsync. Je ne l'ai pas examiné en profondeur, mais je le vois mentionné tout le temps. apparemment c'est assez bon.