Les approches typiques recommandent de lire le binaire via FileStream et de le comparer octet par octet.
- Une comparaison de somme de contrôle telle que CRC serait-elle plus rapide?
- Existe-t-il des bibliothèques .NET qui peuvent générer une somme de contrôle pour un fichier?
Réponses:
Une comparaison de somme de contrôle sera probablement plus lente qu'une comparaison octet par octet.
Afin de générer une somme de contrôle, vous devrez charger chaque octet du fichier et y effectuer un traitement. Vous devrez ensuite le faire sur le deuxième fichier. Le traitement sera presque certainement plus lent que le contrôle de comparaison.
Quant à générer une somme de contrôle: vous pouvez le faire facilement avec les classes de cryptographie. Voici un petit exemple de génération d'une somme de contrôle MD5 avec C #.
Cependant, une somme de contrôle peut être plus rapide et avoir plus de sens si vous pouvez pré-calculer la somme de contrôle du cas "test" ou "de base". Si vous avez un fichier existant et que vous vérifiez si un nouveau fichier est le même que le fichier existant, le pré-calcul de la somme de contrôle sur votre fichier "existant" signifierait seulement avoir besoin de faire le DiskIO une fois, sur le nouveau fichier. Ce serait probablement plus rapide qu'une comparaison octet par octet.
la source
La méthode la plus lente possible consiste à comparer deux fichiers octet par octet. Le plus rapide que j'ai pu trouver est une comparaison similaire, mais au lieu d'un octet à la fois, vous utiliseriez un tableau d'octets dimensionné à Int64, puis compareriez les nombres résultants.
Voici ce que j'ai trouvé:
Lors de mes tests, j'ai pu voir que cela surclassait un scénario ReadByte () simple de presque 3: 1. En moyenne sur 1000 exécutions, j'ai obtenu cette méthode à 1063 ms et la méthode ci-dessous (comparaison simple octet par octet) à 3031 ms. Le hachage revenait toujours en moins de seconde à environ 865 ms en moyenne. Ce test était avec un fichier vidéo d'environ 100 Mo.
Voici les méthodes ReadByte et de hachage que j'ai utilisées, à des fins de comparaison:
la source
FilesAreEqual_Hash
méthode doit également avoir unusing
sur les deux flux de fichiers comme laReadByte
méthode, sinon elle s'accrochera aux deux fichiers.FileStream.Read()
peut en fait lire moins d'octets que le nombre demandé. Vous devriez utiliser à laStreamReader.ReadBlock()
place.Si vous ne vous décidez vraiment besoin d' une comparaison octet par octet complet (voir d' autres réponses pour la discussion de hashing), la solution la plus simple est:
• pour les
System.IO.FileInfo
instances:• pour les
System.String
noms de chemin:Contrairement à d'autres réponses postées, c'est formellement correct pour tout type de fichier: binaire, texte, médias, exécutable, etc., mais comme un complet binaire comparaison , les fichiers qui diffèrent uniquement par des moyens « sans importance » (comme nomenclature , ligne -envoi , encodage de caractères , métadonnées multimédia, espaces, remplissage, commentaires de code source, etc.) seront toujours considérés comme non égaux .
Ce code charge entièrement les deux fichiers en mémoire, il ne doit donc pas être utilisé pour comparer des fichiers vraiment gigantesques . Au-delà de cette mise en garde importante, le chargement complet n'est pas vraiment une pénalité compte tenu de la conception du .NET GC (car il est fondamentalement optimisé pour garder de petites allocations de courte durée extrêmement bon marché ), et en fait pourrait même être optimal lorsque la taille des fichiers est attendue être inférieur à 85K , car en utilisant un minimum de code utilisateur (comme indiqué ici) implique de déléguer au maximum les problèmes de performances des fichiers au
CLR
,BCL
etJIT
à bénéficier de (par exemple) la dernière technologie de conception, le code du système et l' optimisation d'exécution adaptative.En outre, pour de tels scénarios quotidiens, les préoccupations concernant les performances de la comparaison octet par octet via des
LINQ
énumérateurs (comme indiqué ici) sont sans objet, car frapper le disque a̲t̲ a̲l̲l̲ pour les E / S de fichier éclipsera, de plusieurs ordres de grandeur, les avantages des différentes alternatives de comparaison de mémoire. Par exemple, même si nousSequenceEqual
donne en fait "l'optimisation" de l' abandon au premier décalage , cela n'a guère d'importance après avoir déjà récupéré le contenu des fichiers, chacun étant entièrement nécessaire pour confirmer la correspondance.la source
En plus de la réponse de Reed Copsey :
Le pire des cas est celui où les deux fichiers sont identiques. Dans ce cas, il est préférable de comparer les fichiers octet par octet.
Si si les deux fichiers ne sont pas identiques, vous pouvez accélérer un peu les choses en détectant plus tôt qu'ils ne sont pas identiques.
Par exemple, si les deux fichiers sont de longueur différente, vous savez qu'ils ne peuvent pas être identiques et vous n'avez même pas besoin de comparer leur contenu réel.
la source
Cela devient encore plus rapide si vous ne lisez pas par petits morceaux de 8 octets, mais mettez une boucle autour, en lisant un plus gros morceau. J'ai réduit le temps de comparaison moyen à 1/4.
la source
count1 != count2
n'est pas correcte.Stream.Read()
peut renvoyer moins que le décompte que vous avez fourni, pour diverses raisons.Int64
blocs, vous pouvez calculer la taille comme ceci:const int bufferSize = 1024 * sizeof(Int64)
.La seule chose qui pourrait rendre une comparaison de somme de contrôle légèrement plus rapide qu'une comparaison octet par octet est le fait que vous lisez un fichier à la fois, ce qui réduit quelque peu le temps de recherche de la tête de disque. Ce léger gain peut cependant très bien être mangé par le temps supplémentaire de calcul du hachage.
De plus, une comparaison de somme de contrôle n'a bien sûr aucune chance d'être plus rapide si les fichiers sont identiques. Si ce n'est pas le cas, une comparaison octet par octet se terminerait à la première différence, ce qui la rendrait beaucoup plus rapide.
Vous devez également considérer qu'une comparaison de code de hachage vous indique uniquement qu'il est très probable que les fichiers soient identiques. Pour être sûr à 100%, vous devez effectuer une comparaison octet par octet.
Si le code de hachage, par exemple, est de 32 bits, vous êtes certain à environ 99,99999998% que les fichiers sont identiques si les codes de hachage correspondent. C'est près de 100%, mais si vous avez vraiment besoin d'une certitude à 100%, ce n'est pas tout.
la source
1 - (1 / (2^32))
, qui est la probabilité qu'un seul fichier ait un hachage 32 bits donné. La probabilité que deux fichiers différents aient le même hachage est la même, car le premier fichier fournit la valeur de hachage «donnée», et nous devons seulement considérer si l'autre fichier correspond ou non à cette valeur. Les chances de hachage 64 et 128 bits diminuent à 99,999999999999999994% et 99,9999999999999999999999999999999999997% (respectivement), comme si cela importait avec des nombres aussi insondables.Edit: Cette méthode ne fonctionnerait pas pour comparer des fichiers binaires!
Dans .NET 4.0, la
File
classe a les deux nouvelles méthodes suivantes:Ce qui signifie que vous pouvez utiliser:
la source
Honnêtement, je pense que vous devez élaguer votre arbre de recherche autant que possible.
Choses à vérifier avant de passer octet par octet:
En outre, la lecture de gros blocs à la fois sera plus efficace car les lecteurs lisent plus rapidement les octets séquentiels. Le passage octet par octet entraîne non seulement beaucoup plus d'appels système, mais il incite également la tête de lecture d'un disque dur traditionnel à faire des va-et-vient plus fréquents si les deux fichiers sont sur le même lecteur.
Lisez les blocs A et B dans un tampon d'octets et comparez-les (n'utilisez PAS Array.Equals, voir les commentaires) Ajustez la taille des blocs jusqu'à ce que vous atteigniez ce que vous pensez être un bon compromis entre la mémoire et les performances. Vous pouvez également multi-threader la comparaison, mais ne pas multi-thread les lectures de disque.
la source
Ma réponse est un dérivé de @lars mais corrige le bogue dans l'appel à
Stream.Read
. J'ajoute également une vérification rapide des autres réponses et une validation d'entrée. En bref, cela devrait être la réponse:Ou si vous voulez être super génial, vous pouvez utiliser la variante async:
la source
Mes expériences montrent qu'il est vraiment utile d'appeler Stream.ReadByte () moins de fois, mais utiliser BitConverter pour empaqueter des octets ne fait pas beaucoup de différence par rapport à la comparaison d'octets dans un tableau d'octets.
Il est donc possible de remplacer cette boucle "Math.Ceiling and iterations" dans le commentaire ci-dessus par la plus simple:
Je suppose que cela a à voir avec le fait que BitConverter.ToInt64 doit faire un peu de travail (vérifier les arguments puis effectuer le décalage de bits) avant de comparer et cela finit par être la même quantité de travail que comparer 8 octets dans deux tableaux .
la source
Si les fichiers ne sont pas trop volumineux, vous pouvez utiliser:
Il ne sera possible de comparer les hachages que si les hachages sont utiles à stocker.
(Modification du code en quelque chose de beaucoup plus propre.)
la source
Une autre amélioration sur les fichiers volumineux de longueur identique peut être de ne pas lire les fichiers séquentiellement, mais plutôt de comparer des blocs plus ou moins aléatoires.
Vous pouvez utiliser plusieurs threads, en commençant à différentes positions dans le fichier et en comparant vers l'avant ou vers l'arrière.
De cette façon, vous pouvez détecter les changements au milieu / à la fin du fichier, plus rapidement que vous ne le feriez en utilisant une approche séquentielle.
la source
Si vous avez seulement besoin de comparer deux fichiers, je suppose que le moyen le plus rapide serait (en C, je ne sais pas si cela s'applique à .NET)
OTOH, si vous avez besoin de trouver s'il y a des fichiers en double dans un ensemble de N fichiers, le moyen le plus rapide est sans aucun doute d'utiliser un hachage pour éviter les comparaisons bit par bit à N voies.
la source
Quelque chose (espérons-le) raisonnablement efficace:
la source
Voici quelques fonctions utilitaires qui vous permettent de déterminer si deux fichiers (ou deux flux) contiennent des données identiques.
J'ai fourni une version "rapide" qui est multithread car elle compare des tableaux d'octets (chaque tampon rempli à partir de ce qui a été lu dans chaque fichier) dans différents threads en utilisant des tâches.
Comme prévu, il est beaucoup plus rapide (environ 3 fois plus rapide) mais il consomme plus de CPU (car il est multi-thread) et plus de mémoire (car il a besoin de deux tampons de tableau d'octets par thread de comparaison).
la source
Je pense qu'il y a des applications où le "hachage" est plus rapide que la comparaison octet par octet. Si vous avez besoin de comparer un fichier avec d'autres ou d'avoir une miniature d'une photo qui peut changer. Cela dépend de l'endroit et de la manière dont il est utilisé.
Ici, vous pouvez obtenir ce qui est le plus rapide.
En option, nous pouvons enregistrer le hachage dans une base de données.
J'espère que cela peut aider
la source
Encore une autre réponse, dérivée de @chsh. MD5 avec utilisations et raccourcis pour le fichier même, le fichier n'existe pas et différentes longueurs:
la source
if (i>=secondHash.Length ...
Dans quelles circonstances deux hachages MD5 auraient-ils des longueurs différentes?Cela fonctionne bien en comparant d'abord la longueur sans lire les données, puis en comparant la séquence d'octets de lecture
la source