Comment comparer des fichiers texte volumineux?
J'ai une question d'ordre général sur votre avis au sujet de ma "technique".
Il y a 2 fichiers texte (file_1
et file_2
) qui doivent être comparés les uns aux autres. Les deux sont très gros (3 à 4 giga-octets, à partir de 30 000 000 à 45 000 000 de lignes de chaque).
Mon idée est de lire plusieurs lignes (autant que possible) de file_1
de la mémoire, puis de comparer ces tous lignes de file_2
. Si il y a un match, les lignes de deux fichiers qui correspondent doit être écrit dans un nouveau fichier. Ensuite, allez sur les 1000 lignes de file_1
et aussi de comparer ces tous lignes de file_2
jusqu'à ce que je suis allé à travers file_1
complètement.
Mais cela semble vraiment très, très fastidieux et compliqué pour moi.
Pouvez-vous penser à d'autres méthode pour comparer ces deux fichiers?
Combien de temps pensez-vous de la comparaison pourrait prendre?
Pour mon programme, le temps n'a pas beaucoup d'importance. Je n'ai aucune expérience dans le travail avec des fichiers énormes, donc je n'ai aucune idée de combien de temps cela pourrait prendre. Il ne devrait pas prendre plus d'une journée. 😉 Mais j'ai peur que mon technique pourrait prendre une éternité...
Antoher question qui vient à l'esprit: combien de lignes que vous lisez dans la mémoire? Autant que possible? Est-il un moyen de déterminer le nombre de lignes possibles avant de l'essayer?
Je veux en lire autant que possible (parce que je pense que c'est plus rapide), mais j'ai manqué de mémoire assez souvent.
Merci d'avance.
MODIFIER
Je pense que je dois expliquer mon problème un peu plus.
Le but n'est pas de voir si les deux fichiers sont en général identiques (ils ne le sont pas).
Il y a quelques lignes dans chaque fichier qui partagent les mêmes "caractéristiques".
Voici un exemple:
file_1
ressemble un peu à ceci:
mat1 1000 2000 TEXT //this means the range is from 1000 - 2000
mat1 2040 2050 TEXT
mat3 10000 10010 TEXT
mat2 20 500 TEXT
file_2
ressemble à ceci:
mat3 10009 TEXT
mat3 200 TEXT
mat1 999 TEXT
TEXT
se réfère aux chiffres et caractères qui ne sont d'aucun intérêt pour moi, mat
peut aller de mat1 - mat50
et sont dans aucun ordre; il peut aussi y avoir 1000x mat2
(mais les chiffres dans la colonne suivante sont différents). J'ai besoin de trouver le raccord des lignes de manière à: matX est le même dans les deux lignes par rapport à un le nombre mentionné dans file_2
s'inscrit dans la plage mentionnée dans file_1
.
Donc, dans mon exemple je voudrais trouver un match: la ligne 3 de file_1
et la ligne 1 de file_2
(parce que les deux sont mat3 et 10009 est compris entre 10 000 et 10010).
J'espère de ce fait, il est clair pour vous!
Donc ma question est: comment voulez-vous de recherche pour la mise en correspondance de lignes?
Oui, je utiliser Java comme mon langage de programmation.
MODIFIER
J'ai maintenant divisé les fichiers de grande première, de sorte que je n'ai pas de problèmes de mémoire. Je pense aussi que c'est plus rapide de comparer les (nombreuses) des fichiers plus petits les uns des autres que ces deux fichiers énormes. Après ce que je peux comparer la façon dont je l'ai mentionné ci-dessus. Il peut ne pas être la manière parfaite, mais je suis encore à apprendre 😉
Mais tous vos approches ont été très utile pour moi, merci pour vos réponses!
java
cela veut-il dire que vous ne voulez le faire en Java?Je ne sais pas si cela peut vous aider stackoverflow.com/questions/964332/...
Sonne comme un bon cas d'utilisation pour le mappage de mémoire (et de défragmenter vos fichiers en premier), mais je ne sais pas si Java propose que.
Pas sûr de comprendre votre condition. Avez-vous besoin de trouver des lignes qui sont en commun entre les deux fichiers? Ou êtes-vous vraiment essayer de faire une diff?
Dans ce cas, vous prétraiter file_2 de sorte que vous avez 50 structures de données (mat1..mat50) chacune avec une série de plages commandé par la limite inférieure de sorte que vous pouvez faire une recherche binaire sur elle. Ne devrait pas prendre plus de 1 go pour 40.000.000 lignes. Ensuite, passez par file_1 de façon séquentielle et chercher chaque ligne.
OriginalL'auteur Grrace | 2011-08-18
Vous devez vous connecter pour publier un commentaire.
Maintenant que vous avez donné plus de détails, l'approche que je prendrais repose sur pré-partitionnement, et, éventuellement, le tri avant de chercher des allumettes.
Cela devrait éliminer une quantité importante de comparaisons qui n'auraient autrement pas de match de toute façon dans la naïve, la force brute de l'approche. Pour la clarté de l'exposé, permet de peg à la fois des fichiers à 40 millions de lignes chacun.
Partitionnement: de Lire à travers
file_1
et envoyer toutes les lignes commençant parmat1
àfile_1_mat1
, et ainsi de suite. Faire de même pourfile_2
. C'est trivial, avec un peu degrep
, ou si vous désirez le faire par programmation en Java, c'est un débutant de l'exercice.C'est un passage dans deux fichiers pour un total de 80million lignes lire, produisant deux ensembles de 50 dossiers de 800 000 lignes chacun en moyenne.
Tri: Pour chaque partition, les trier en fonction de la valeur numérique dans la deuxième colonne (la limite inférieure de
file_1
et le nombre réel defile_2
). Même si de 800 000 lignes ne peut pas tenir en mémoire je suppose que nous pouvons nous adapter 2-voie externe, de fusion de tri et d'effectuer plus rapidement (moins de lectures) qu'une sorte de ensemble l'espace non partitionné.Comparaison: Maintenant, vous avez juste à itérer une fois par les deux paires de
file_1_mat1
etfile_2_mat1
, sans besoin de garder quelque chose en mémoire, la sortie correspond à votre fichier de sortie. Répétez l'opération pour le reste des partitions à son tour. Pas besoin d'un final "fusionner" l'étape (sauf si vous êtes de traitement des partitions en parallèle).Même sans l'étape du triage de la naïveté de comparaison vous êtes déjà en train de le doit travailler plus vite dans plus de 50 paires de fichiers et 800 000 lignes chacun, plutôt que d'avoir deux fichiers avec 40 millions de lignes chacun.
OriginalL'auteur Alistair A. Israel
Je pense, votre chemin est plutôt raisonnable.
Je peux imaginer des stratégies différentes, par exemple, vous pouvez trier les deux fichiers avant de les comparer (où est mise en œuvre efficace de filesort, et unix sorte utilitaire pouvez trier des fichiers de plusieurs go en quelques minutes), et, alors triés, vous pouvez comparer les fichiers sequentally, la lecture ligne par ligne.
Mais c'est assez complexe à faire, vous devez exécuter le programme externe (tri), ou écrire comparable mise en œuvre efficace de filesort en java par vous-même-qui n'est en soi pas une tâche facile. Ainsi, par souci de simplicité, je pense que vous le moyen de fragments de lire est très prometteur;
Quant à la façon de trouver raisonnable bloc-tout d'abord, il peut ne pas être correcte de ce qu'est "la plus-mieux" - je pense, le temps de travail va croître à l'infini, dans une certaine ligne constante. Donc, peut-être, vous serez proche de celle de la ligne plus vite que vous pensez -- vous avez besoin de référence pour cette.
-- Vous pouvez lire les lignes de la mémoire tampon comme ceci:
Si vous lisez autant de lignes que vous le pouvez, laissant un dernier TAILLE_BLOC de libérer de la mémoire. TAILLE_BLOC doit être grand enouth pour le reste de votre programme à s'exécuter sans OOM
OriginalL'auteur BegemoT
Dans un monde idéal, vous devez être capable de lire à chaque ligne de file_2 en mémoire (probablement à l'aide d'une recherche rapide de l'objet comme un
HashSet
, selon vos besoins), puis lire chaque ligne de file_1 un à la fois et de le comparer à votre structure de données de la tenue de la lignes de file_2.Comme vous l'avez dit vous manquez de mémoire cependant, je pense que diviser et conquérir de type stratégie serait la meilleure. Vous pouvez utiliser la même méthode, comme je l'ai mentionné ci-dessus, mais le lire en une de la moitié (ou un tiers, d'un quart... selon la quantité de mémoire que vous pouvez utiliser) des lignes à partir de file_2 et de les stocker, puis de comparer l'ensemble des lignes file_1. Puis lire dans le prochain demi/tiers/quart/whatever dans la mémoire (en remplacement de l'ancien lignes) et de passer par file_1 de nouveau. Cela signifie que vous devez passer par file_1 plus, mais vous devez travailler avec votre mémoire contraintes.
EDIT: En réponse à l'ajout de détails à votre question, je voudrais changer ma réponse dans la partie. Au lieu de lire dans tous file_2 (ou en morceaux) et dans la lecture de file_1 une ligne à la fois, à l'inverse, comme file_1 contient les données à vérifier.
Également, en ce qui concerne la recherche de la correspondance des lignes. Je pense que le meilleur moyen serait de faire un traitement sur file_1. Créer un
HashMap<List<Range>>
que des cartes d'une Chaîne de caractères ("mat1" - "mat50") à une liste deRange
s (juste un wrapper pour un startOfRangeint
et un endOfRangeint
) et de le remplir avec les données de file_1. Ensuite, écrivez une fonction (en ignorant la vérification des erreurs)et de l'appeler pour chaque (analysé) de la ligne de file_2.
OriginalL'auteur epochengine
c'est un compromis: si vous lisez un gros morceau de fichier, vous enregistrez le disque le temps de recherche, mais vous avez lu les informations que vous n'aurez pas besoin, depuis le changement a été rencontrée sur les premières lignes.
Vous devriez lancer quelques expériences [repères], avec des variations de taille de bloc, pour savoir quel est le meilleur morceau de lire, dans la moyenne des cas.
OriginalL'auteur amit
Pas sûr de savoir comment bien une réponse ce serait, mais jetez un oeil à cette page: http://c2.com/cgi/wiki?DiffAlgorithm - il résume un peu diff algorithmes. Chasse-McIlroy algorithme est probablement la meilleure mise en œuvre. À partir de cette page il y a également un lien vers une implémentation java de la GNU diff. Cependant, je pense qu'une mise en œuvre en C/C++ et compilé en code natif sera beaucoup plus rapide. Si vous êtes coincé avec java, vous pouvez envisager de JNI.
Je n'ai pas essayé, mais il peut être un bon test à exécuter.
Sur mes 4 go de PC, un diff sur les 350.000 ligne des fichiers déjà échoué. Devinez combien de mémoire vous auriez besoin si l'exigence de mémoire pousse juste linéaire!
OriginalL'auteur Aleks G
En effet, qui pourrait prendre un certain temps. Vous devez faire de 1 200.000,000 ligne comparisions.
Il existe plusieurs possibilités pour la vitesse que par un ordre de magnitute:
Un tri fichier2 et faire une sorte de recherche binaire sur fichier.
Une autre approche: calculer une somme de contrôle de chaque ligne et de recherche. En fonction d'une moyenne de longueur de ligne, le fichier en question serait beaucoup plus petite et vous ne pouvez vraiment faire une recherche binaire si vous stockez les sommes de contrôle dans un format fixe (c'est à dire une longue)
Le nombre de lignes que vous lisez à la fois à partir file_1 ne pas d'importance, cependant. C'est la micro-optimisation dans le visage d'une grande complexité.
OriginalL'auteur Ingo
Si vous voulez une approche simple: vous pouvez hachage des fichiers et de comparer le hash. Mais c'est probablement plus rapide (surtout si les fichiers sont différents) pour l'utilisation de votre approche. À propos de la consommation de mémoire: assurez-vous d'utiliser suffisamment de mémoire, sans utiliser de tampon pour ce genre de chose est une mauvaise idée..
Et toutes ces réponses sur les tables de hachage, les sommes de contrôle etc: ce ne sont pas des plus rapides. Vous avez qu'à lire l'ensemble du dossier dans les deux cas. Avec des hachages/sommes de contrôle vous avez même de calculer quelque chose...
OriginalL'auteur duedl0r
Ce que vous pouvez faire est de trier chaque fichier individuel. par exemple, l'UNIX
sort
ou similaire à Java. Vous pouvez lire les fichiers triés une ligne à la fois pour effectuer une fusion de tri.OriginalL'auteur Peter Lawrey
Je n'ai jamais travaillé avec de gros fichiers, mais c'est mon idée et devrait fonctionner.
Vous pourrait envisager de hachage. À l'aide de Hachage SHA-1.
Importer les suivants
Une fois votre fichier texte, etc a été chargé de l'avoir en boucle sur chaque ligne et à la fin d'imprimer la table de hachage. L'exemple des liens ci-dessous permettra d'aller plus en profondeur.
SHA exemple de Code en se concentrant sur Fichier Texte
DONC, la Question sur le calcul de SHA en JAVA (Éventuellement utile)
Un autre exemple de code de hachage.
Simple lire chaque fichier seperatley, si la valeur de hachage pour chaque fichier est le même à la fin du processus, alors que les deux fichiers sont identiques. Si non, alors quelque chose est incorrect.
Alors si vous obtenez une valeur différente que vous pouvez faire le super temps, ligne par ligne, vérifier.
Dans l'ensemble, Il semble que la lecture ligne par ligne par ligne par ligne, etc prendrait une éternité. Je voudrais faire cela si vous essayez de trouver les différences individuelles. Mais je pense que le hachage serait plus rapide pour voir si elles sont la même chose.
La somme de contrôle SHA
OriginalL'auteur sealz
Si vous voulez savoir exactement si les fichiers sont différents ou non, puis il n'y a pas une solution meilleure que la vôtre -- comparant de manière séquentielle.
Cependant, vous pouvez faire quelques heuristiques qui peuvent vous dire avec une sorte de probabilité si les fichiers sont identiques.
1) Vérifiez la taille du fichier, c'est le plus simple.
2) Prendre un fichier aléatoire de la position et de comparer bloc d'octets commençant à cette position dans les deux fichiers.
3) Répétez l'étape 2) réaliser la nécessaire probabilité.
Vous devez calculer et tester le nombre de lectures (et de la taille de bloc) sont utiles pour votre programme.
OriginalL'auteur Mariy
Ma solution serait de produire un index d'un fichier, d'abord, puis l'utiliser pour faire la comparaison. C'est à l'instar de certaines autres réponses en ce qu'il utilise le hachage.
Vous mentionner que le nombre de lignes est à environ 45 millions de dollars. Cela signifie que vous pourrait (éventuellement) de stocker un index qui utilise 16 octets par entrée (128 bits) et elle serait d'utiliser environ 45 000 000 d'*16 = ~685MB de RAM, ce qui n'est pas déraisonnable sur un système moderne. Il y a des frais fixes en utilisant la solution que je décris ci-dessous, vous pouvez encore le trouver, vous devez utiliser d'autres techniques telles que les fichiers mappés en mémoire ou sur disque tables pour créer l'index. Voir Hypertable ou HBase pour un exemple de la façon de stocker l'index dans un disque rapide à base de la table de hachage.
Donc, dans son intégralité, l'algorithme serait quelque chose comme:
EDIT:
En réponse à votre édité question, ce ne serait pas vraiment l'aider en lui-même. Vous pouvez simplement de hachage de la première partie de la ligne, mais ce ne serait que la création de 50 entrées différentes. Vous pouvez ensuite créer un autre niveau dans la structure de données, ce qui permettrait de carte le début de chaque plage pour le décalage de la ligne d'où elle vient.
Donc quelque chose comme
index.get("mat32")
serait de retour un TreeMap des plages. Vous pourriez regarder pour la gamme précédente la valeur que vous êtes à la recherche pour lowerEntry(). Ensemble, ce serait vous donner une assez rapide pour vérifier si une donnée matX/nombre de combinaison a été dans l'une des plages que vous avez verifié.OriginalL'auteur Mike Houston
essayez d'éviter la consommation de mémoire et en faire un disque de consommer.
je veux dire diviser chaque fichier en chargeable la taille des pièces et de les comparer, cela peut prendre un certain temps, mais vous gardera à l'abri de traiter avec les limites de la mémoire.
OriginalL'auteur Jacer Omri
Que penser de l'utilisation de la source de contrôle, comme Mercurial? Je ne sais pas, peut-être que ce n'est pas exactement ce que vous voulez, mais c'est un outil qui est conçu pour suivre les changements entre deux révisions. Vous pouvez créer un référentiel, de commettre le premier fichier, puis le remplacer par un autre, un commit la seconde:
De là, vous pouvez obtenir un diff, vous dire ce que les lignes diffèrent. Si vous pourrait en quelque sorte de l'utiliser diff pour déterminer ce que les lignes étaient les mêmes, vous être tous ensemble.
C'est juste une idée, quelqu'un me corrige si je me trompe.
mais sur ces énormes fichiers, diff probablement ne fonctionnera pas bien.
OriginalL'auteur Igor Zinov'yev
Je voudrais essayer le suivant: pour chaque fichier que vous les comparer, de les créer les fichiers temporaires (j'en parle comme d'partielle de fichier version ultérieure) sur le disque représentant chaque lettre alphabétique et un fichier supplémentaire pour tous les autres caractères. ensuite, lisez la totalité du fichier ligne par ligne. pour ce faire, insérez la ligne suivante dans le fichier qui correspond à la lettre, il commence avec. depuis que vous l'avez fait pour les deux fichiers, vous pouvez désormais de limiter la comparaison pour le chargement de deux petits fichiers à la fois. une ligne commençant par un, par exemple, peut apparaître que dans un seul fichier partiel et il n'y aura pas besoin de comparer chaque fichier partiel plus d'une fois. Si les fichiers sont encore très grand, vous pouvez appliquer la même méthode pour les fichiers partiels (lettre de fichiers spécifiques) qui sont comparés par la création de fichiers en fonction de la deuxième lettre. le commerce de ici serait l'utilisation d'un grand espace disque temporairement jusqu'à ce que le processus est terminé. dans ce processus, les approches mentionnées dans d'autres posts ici peut aider à traiter avec les fichiers partiels de manière plus efficace.
OriginalL'auteur