Un de mes énormes fichiers texte (jusqu’à 2 Gio) contient environ 100 doublons exacts de chaque ligne qu’il contient (inutile dans mon cas, car le fichier est une table de données de type CSV).
Ce dont j'ai besoin, c’est d’éliminer toutes les répétitions (de préférence, mais cela peut être sacrifié pour une amélioration significative des performances) en maintenant l’ordre de séquence original. Dans le résultat, chaque ligne doit être unique. S'il y a 100 lignes égales (généralement, les doublons sont répartis dans le fichier et ne seront pas voisins), il ne restera plus qu'une seule de ces lignes.
J'ai écrit un programme en Scala (considérez-le en Java si vous ne connaissez pas Scala) pour l'implémenter. Mais peut-être existe-t-il des outils natifs C-écrits plus rapides capables de le faire plus rapidement?
MISE À JOUR: la awk '!seen[$0]++' filename
solution semblait bien fonctionner pour moi tant que les fichiers étaient proches de 2 Gio ou moins, mais maintenant que je vais nettoyer un fichier de 8 Gio, cela ne fonctionne plus. Cela semble prendre une infinité sur un Mac avec 4 Go de RAM et un PC Windows 7 64 bits avec 4 Go de RAM et 6 Go échangés, la mémoire manque. Et je ne suis pas enthousiaste à l'idée d'essayer Linux avec 4 Go de RAM, compte tenu de cette expérience.
sort -u
sera probablement plus rapide.Réponses:
Une
awk
solution vue sur #bash (Freenode):la source
awk
version plus détaillée utilisant 2 recherches sur un tableau (expliquée plus en détail dans la réponse de Gilles): 0m36.132s vs 0m49.958s .. pour 50 millions de lignes .. Je pensais que le goulot d’étranglement serait l’E / S, mais la recherche de tableau supplémentaire est ... 1 million d'éléments dans le tableau semble avoir uneIl existe une méthode simple (qui n’est pas évidente), qui utilise des utilitaires standard et qui ne nécessite pas une mémoire importante
sort
, mais qui, dans la plupart des implémentations, offre des optimisations spécifiques pour les fichiers volumineux (un bon algorithme de tri externe). Un avantage de cette méthode est qu’elle ne boucle que sur toutes les lignes des utilitaires spéciaux, jamais des langages interprétés.Si toutes les lignes commencent par un caractère non-blanc, vous pouvez vous passer de certaines des options suivantes:
Pour une grande quantité de duplication, une méthode qui nécessite uniquement de stocker une copie de chaque ligne en mémoire fonctionnera mieux. Avec quelques frais d'interprétation, il y a un script awk très concis pour cela (déjà posté par enzotib ):
De manière moins concise: affichez
!seen[$0] {print} {seen[$0] += 1}
la ligne courante si elle n’a pas encore été vue, puis incrémentez leseen
compteur de cette ligne (les variables non initialisées ou les éléments de tableau ont la valeur numérique 0).Pour les longues lignes, vous pouvez économiser de la mémoire en ne conservant qu'une somme de contrôle non spoofable (par exemple, un résumé cryptographique) de chaque ligne. Par exemple, avec SHA-1, vous n'avez besoin que de 20 octets plus un temps système constant par ligne. Mais l'informatique digère est plutôt lente; cette méthode ne gagnera que si vous avez un processeur rapide (en particulier avec un accélérateur matériel pour calculer les résumés) et pas beaucoup de mémoire par rapport à la taille du fichier et des lignes suffisamment longues. Aucun utilitaire de base ne vous permet de calculer une somme de contrôle pour chaque ligne; vous devrez supporter les frais d'interprétation de Perl / Python / Ruby /… ou écrire un programme compilé dédié.
la source
awk '!seen[$0]++'
, cela signifie-t-il que si awk voit 2 lignes en double, il gardera toujours la première et ignorera toutes les lignes suivantes? (Ou il gardera le dernier?)sort -u
change l'ordre. Ma réponse montre des solutions qui préservent l'ordre (l'ordre des premières occurrences, pour être précis).Notez que le fichier de sortie sera trié.
la source
awk
commande dans d'autres réponses, mais conceptuellement simple!sort -u
pour supprimer les doublons pendant le tri, plutôt qu'après. (Et économise de la bande passante mémoire) en le dirigeant vers un autre programme). Ce n'est que mieux que laawk
version si vous voulez que votre sortie soit triée aussi. (Le PO sur cette question veut que sa commande originale soit préservée , alors c'est une bonne réponse pour un cas d'utilisation légèrement différent.)En supposant que vous puissiez vous permettre de conserver autant que le fichier dédupliqué en mémoire (si vos données sont effectivement dupliquées par un facteur 100, cela devrait représenter environ 20 Mo + de temps système), vous pouvez le faire très facilement avec Perl.
Cela préserve l'ordre aussi.
Vous pouvez extraire le nombre d'occurrences de chaque ligne du
%dup
hachage si vous le souhaitez, sous forme de bonus gratuit.Si vous préférez
awk
, vous devriez le faire aussi (même logique que la version de Perl, même ordre, mêmes données rassemblées dans ladup
variable):la source
uniq
fait-il tout seulAucune autre réponse n'ayant fourni de support sur place, en voici une:
la source
GNU Awk 4.0.2
Vous pouvez utiliser
uniq
http://www.computerhope.com/unix/uuniq.htmuniq
signale ou filtre les lignes répétées d'un fichier.la source
'uniq' does not detect repeated lines unless they are adjacent.
vous devez donc d'abord le trier et perdre l'ordre des lignes non dupliquées.Python One liners:
la source
OrderedDict
Aucune des réponses ici ne fonctionnant pour moi sur mon Mac, j'ai donc écrit un script python simple qui fonctionne pour moi. J'ignore les espaces de début et de fin et je ne me soucie pas de la consommation de mémoire.
Enregistrez ce qui précède dans unique.py et exécutez-le comme ceci:
la source
Avec bash 4, une solution pure-bash tirant parti des tableaux associatifs peut être utilisée. Voici un exemple
la source
read
boucles pour traiter de gros fichiers texte. bash doit lire un octet à la fois pour éviter de dépasser une nouvelle ligne. Bash n’est pas très rapide en traitement de texte en général par rapport à awk. Si vous utilisez ceci, vousread -ra
éviterez de manger des barres obliques inverses dans votre entrée. N'oubliez pas non plusunset llist
après la boucle, si vous mettez ceci dans une fonction shell ou si vous l'utilisez de manière interactive.