Forum Programmation.shell Aide sur commande uniq pour dédoublonner fichier

Posté par  . Licence CC By‑SA.
Étiquettes :
0
18
déc.
2014

Bonjour,

Je dois supprimer les doublons dans un fichier texte assez gros (300Mo) en respectant un masque particulier.

Exemple de lignes contenues dans le fichier :

03A99999999   2015-01-08-00.00.01.000000+000076800,00020150109

Pour la suppression des doublons je ne dois pas tenir compte des caractères 14 à 40.

Avez vous une idée de comment appliquer ce masque dans uniq ?

Par avance, merci.

  • # uniq seul ne suffira pas

    Posté par  . Évalué à 3. Dernière modification le 18 décembre 2014 à 17:00.

    plusieurs solutions : faire du perl ou du sed (ou du awk), modifier le fichier (uniq peut ignorer les premiers champs/caractères) mais bon mois je ferai plutôt du perl

    de tête sans tester (donc à tester)
    je précise que comme uniq il s'attend à un fichier trié;

    #! /usr/bin/perl
    
    use strict;
    
    my prevLine="";
    while(<>)
    {
       /(.{13}).{26}(.*)/ 
       my $pattern="$1$2";
       print $_ unless ( $prevLine eq $pattern );
       $prevLine=$pattern;
    }

    ps la regex pourrait probablement être remplacé par

    /(\S+)\s+.*?[+](.*)/

    mais avec une seule ligne d'exemple, je ne peux pas être catégorique

    Il ne faut pas décorner les boeufs avant d'avoir semé le vent

  • # man uniq

    Posté par  . Évalué à 3.

    Bonjour,

    $ man uniq

       -nombre, -f, --skip-fields=nombre
             Ignorer nombre champs avant de vérifier l'unicité. Les nombre premiers champs, et tous les blancs qu'ils contiennent sont ignorés. Les champs sont définis  comme  des
             chaînes de caractères non-blancs, séparées par des espaces ou des tabulations.
    
      +nombre, -s, --skip-chars=nombre
             Ignorer  nombre  caractères  avant  de  vérifier l'unicité. Si l'on utilise conjointement cette option et l'option --skip-fileds, les champs sont sautés préalablement
             avant de sauter les caractères.
    
      -w, --check-chars=nombre
             Ne comparer que nombre caractères de chaque ligne, après avoir éventuellement sauté les champs et les caractères indiqués.
    

    …donc uniq -w 14.

    À noter que uniq compare chaque ligne avec la précédente et ne supprimera pas tes doublons s'ils ne sont pas consécutifs (pour un fichier de log, c'est le comportement attendu). Si c'est nécessaire, il faudra trier ton fichier avec sort mais tu perdras l'ordre chronologique de tes entrées.

    • [^] # Re: man uniq

      Posté par  . Évalué à 3. Dernière modification le 18 décembre 2014 à 17:10.

      Je viens de me rendre compte que ta ligne est plus longue que 40 caractères.

      Si c'est toujours les caractères 14 à 40 que tu veux éliminer, tu peux écrire

      sed -e 's/\(^.\{13\}\).\{27\}\(.*\)$/\1\2/' fichier.log | uniq

  • # Ce que j'ai finalement mis en place

    Posté par  . Évalué à 2.

    #!/usr/bin/env python2
    # -*- coding: utf-8 -*-
    # V0.1 - Julien Pecqueur - 19/12/2014
    
    from sys import stdin, stdout
    
    last_hash = None
    for line in stdin:
        cre = line[0:2]
        if cre == "01":
            cur_hash = hash(line[0:91])
        elif cre == "03":
            cur_hash = hash(line[0:14]+line[40:])
        else:
            cur_hash = hash(line)
        if cur_hash == last_hash:
            continue
        stdout.write(line)
        last_hash = cur_hash
    • [^] # Re: Ce que j'ai finalement mis en place

      Posté par  . Évalué à 2.

      Ça n'est pas bon ! Ou alors tu as mal énoncé ton problème…

      En supposant que ton fichier est trié (sinon ça foire complètement), tu peux avoir la séquence:
      A X a
      A X b
      A Y a

      La première ligne et la dernière sont les mêmes, mais seront les deux conservées. Au lieu de last_hash, utilise une liste dans laquelle tu ajoutes les hashs et que tu testes avec if cur_hash in all_hash:.

      Par ailleurs, tu peux aussi avoir des collisions avec le hash (c'est pour ça qu'il faut pas conduire après), donc dans ton tableau tu peux carrément mettre directement line[0:14]+line[40:] dans la liste. C'est pas très optimisé, mais c'est plus sûr et ça ne tourne qu'une seule fois.

      Et enfin, plutôt qu'une liste, utilise un set, c'est plus adapté.

      • [^] # Re: Ce que j'ai finalement mis en place

        Posté par  . Évalué à 2.

        J'ai oublié de préciser que le 3ème caractère est toujours "A".

        Par contre merci de m'avoir prévenu pour le risque de colision des hashs. Ça m'évitera du débug !

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.