IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Shell et commandes GNU Discussion :

[awk] Comparaison par colonne de deux tableaux


Sujet :

Shell et commandes GNU

  1. #1
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut [awk] Comparaison par colonne de deux tableaux
    Bonjour à tous,

    Je débute sous Awk et j'ai un petit souci concernant l'utilisation des tableaux.

    J'ai deux fichiers CSV en entrée contenant chacun 4 lignes de 5 colonnes (fich1.csv & fich2.csv).

    #cat fich1.csv
    A b c d e
    F g h i j
    K l m n o
    P q r s t

    #cat fich2.csv
    A b c d e
    F g h z j
    K l m n o
    P z r s t

    La colonne 1 (AFKP) sera toujours identique aux deux fichiers .
    Je désire comparer le contenu de chaque colonne afin que le résultat donne :

    L'identifiant F est différent à la colonne 4
    L'identifiant P est différent à la colonne 2

    Je pense que Awk est l'idéal car j'ai des milliers de lignes et ça ma l'air très rapide, par contre je m'arrache les cheveux !
    J'ai réussi à mettre en tableau un fichier mais impossible d'en mettre deux et en encore moins de les comparer.

    Merci d'avance pour vos lumières...

  2. #2
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    salut,

    un fichier CSV c'est censé être des valeurs séparées par des virgules, dans ton exemple tu sépares par des espaces, dans le doute j'ai mis une variable sep modifiable facilement :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    awk -vsep=',' '
    NR == FNR {
      f1[NR] = $0;
      next;
    }
     
    $0 != f1[FNR] {
      n=split(f1[FNR],lf1,sep);
      split($0,lf2,sep);
      for (i=1; i<=n; i++) {
        if (lf1[i] != lf2[i]) {
          printf("L\047identifiant %s est différent à la colonne %d\n", lf1[1], i);
        }
      }
    }' fich1.csv fich2.csv
    (tout le code peut évidemment tenir sur la même ligne)

    on passe les deux fichiers en paramètre directement, la condition NR == FNR est un trick qui permet de rentrer dans le bloc uniquement si le fichier lu est le premier fichier passé en paramètre (donc fic1.csv), à chaque ligne lue on la stocke dans le tableau f1, et on passe à la ligne suivante avec next pour éviter de rentrer dans le second bloc qui lui sert à traiter fic2.csv

    dans le second bloc, on éclate chaque ligne dans un tableau, lf1 pour la ligne issue du fichier fic1.csv, lf2 pour la ligne issue de fic2.csv
    reste plus qu'à comparer chaque colonne dans une boucle for, si les champs sont différents on affiche un message, on notera au passage le \047 en octal pour pouvoir afficher l'apostrophe dans L'identifiant

  3. #3
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    Bonjour

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ cat fi1
    A b c d e
    F g h i j
    K l m n o
    P q r s t
    $ cat fi2
    A b c d e
    F g h z j
    K l m n o
    P z r s t
    $ awk '(NR==FNR){for (i=1;i<=NF;i++) a[FNR,i]=$i;next;} {for (i=1;i<=NF;i++) if ($i!=a[FNR,i]) printf "La ligne %i est différente à la colonne %i.\n",FNR,i;}' fi1 fi2
    La ligne 2 est différente à la colonne 4.
    La ligne 4 est différente à la colonne 2.

  4. #4
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    j'en apprends (ou réapprends) tous les jours, j'avais complètement oublié qu'on pouvait faire ça avec le tableau, bien vu

  5. #5
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut La Ferrari Awk...
    @BufferBob & Flodelarab : Merci infiniment, vous êtes des As !

    J'ai testé et ça marche du feu de Dieu ;
    Des centaines de milliers de lignes traitées en quelques millisecondes, c'est incroyable !

    Je suis surpris chaque jour de la puissance de traitement de Linux en général et de ce langage en particulier.
    Mais j'ai encore du pain sur la planche pour en comprendre toutes les subtilités...

    Par exemple, certaine ligne ont deux champs différents (au lieu de un) et je planche maintenant sur la façon d'arriver à :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #cat fich1.csv
    A;b;c;d;e
    F;g;h;i;j
    K;l;m;n;o
    P;q;z;s;t
     
    #cat fich2.csv
    A;b;c;d;e
    F;z;h;z;j
    K;l;m;n;o
    P;z;r;z;t
     
     
    L'identifiant F est différent à la colonne 2, 4
    L'identifiant P est différent à la colonne 2, 3, 4

    @+
    Lucky

  6. #6
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    Suffit de concaténer.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ cat fi1
    A b c d e
    F g h i j
    K l m n o
    P q r s t
    $ cat fi2
    A b c d e
    F g h z j
    K l m n o
    P j j j j
    $ awk '(NR==FNR){for (i=1;i<=NF;i++) a[FNR,i]=$i;next;} {s="";for (i=1;i<=NF;i++) if ($i!=a[FNR,i]) if (s!="") s=s", "i; else s=i;if (s!="") printf "La ligne %i est différente à la colonne %s.\n",FNR,s;}' fi1 fi2
    La ligne 2 est différente à la colonne 4.
    La ligne 4 est différente à la colonne 2, 3, 4, 5.
    Avec un -F';' en prime, au besoin.

  7. #7
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut La Ferrari Awk...
    @ Flodelarab

    Bien que je ne comprenne pas tout (faut que j'analyse en détail avec mon petit manuel Awk sous le coude), ça marche très bien.

    Par contre comment faire pour afficher le contenu de la colonne 1 (c'est un ID en fait) plutôt que de faire référence au numéro de ligne ?

    Exemple : L'identifiant P est différent à la colonne 2, 3, 4

    Lucky

  8. #8
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    Ton petit manuel va te donner la réponse !

  9. #9
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut La Ferrari Awk...
    Bon j'y suis arrivé un peu au doigt mouillé...

    [...]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if (s!="") printf "Identifiant "a[FNR,1]" (ligne %i) différent à la colonne %s.\n",FNR,s;
    [...]

    Par contre impossible de comprendre pourquoi si j'enlève %i dans le code, je n'ai pas le même résultat...

  10. #10
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    Citation Envoyé par luckythrice Voir le message
    Bien que je ne comprenne pas tout (faut que j'analyse en détail avec mon petit manuel Awk sous le coude), ça marche très bien.

    Par contre comment faire pour afficher le contenu de la colonne 1 (c'est un ID en fait) plutôt que de faire référence au numéro de ligne ?
    tu peux développer le oneliner pour qu'il soit plus lisible :
    Code awk : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    (NR==FNR) {
       for (i=1;i<=NF;i++)
          a[FNR,i]=$i;
       next;
    }
     
    {
       s="";
       for (i=1;i<=NF;i++)
          if ($i!=a[FNR,i])
             if (s!="")
                s=s", "i;
             else
                s=i;
          if (s!="")
             printf "La ligne %i est différente à la colonne %s.\n",FNR,s;
    }

  11. #11
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut La Ferrari Awk...
    Merci BufferBob, c'est ce que j'avais commencé à faire car le "oneliner" comme tu dis, même si ça en jette, pour un non-initié c'est un vrai casse-tête surtout que j'ai l'habitude de bien structurer mes codes comme en Bash ou en PHP.

    Par contre je ne comprend toujours pas pourquoi la suppression de %i me donne un résultat différent ?

  12. #12
    Expert confirmé Avatar de disedorgue
    Homme Profil pro
    Ingénieur intégration
    Inscrit en
    Décembre 2012
    Messages
    4 418
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur intégration
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Décembre 2012
    Messages : 4 418
    Par défaut
    Bonjour,
    Comme tu n'es pas précis dans ton problème concernant le %i, ce que l'on peut juste te dire:
    il est lié a FNR (en fin de ligne) en tant qu'entier et que donc si tu veux le %i, il faut que tu vire aussi le FNR car sinon le %s d'après se rattachera dessus au lieu de la variable s (toujours en fin de ligne)...

  13. #13
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    • if (s!="") printf "Identifiant "a[FNR,1]" (ligne %i) différent à la colonne %s.\n",FNR,s;
      C'est vrai que tu peux récupérer le premier champ de la ligne mémorisée. Mais, tu peux aussi récupérer le premier champ de la ligne en cours: $1 (cf ci-dessous)
    • La fonction "print" écrit une ligne.
      La fonction "printf" écrit une ligne en insérant les paramètres qui suivent le format de la ligne.
      %i, %d, %f, %s, etc sont remplacés par un entier, un décimal, un nombre à virgule, une chaîne de caractère, etc.
      Donc si tu enlèves %i, il n'est pas remplacé.
    • Au final:
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      if (s!="") printf "Identifiant %i (ligne %i) différent à la colonne %s.\n",$1,FNR,s;
    • Pour le développement de la ligne unique, le second "if" a une tabulation de trop puisqu'en dehors de la boucle.

  14. #14
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut
    @ Flodelarab

    Oulala tu m'as complètement paumé avec l'ajout du $1...

    En fait là où j'ai du mal à comprendre c'est comment sont ingurgité les deux fichiers en entrée...

    Dans le code j'ai l'impression qu'il n'y a qu'un seul tableau (a[FNR,i]) alors qu'en temps normal il devrait y en avoir deux !

    Si l'on reprend mon petit exemple :

    #cat fich1.csv
    A;b;c;d;e
    F;g;h;i;j
    K;l;m;n;o
    P;q;z;s;t

    #cat fich2.csv
    A;b;c;d;e
    F;z;h;z;j
    K;l;m;n;o
    P;z;r;z;t

    Comment Awk peut-il distinguer le "h" du premier fichier (a[2,3]) du "h" du second fichier ?

  15. #15
    Expert confirmé Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 041
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 041
    Par défaut
    Citation Envoyé par luckythrice Voir le message
    En fait là où j'ai du mal à comprendre c'est comment sont ingurgité les deux fichiers en entrée...
    Citation Envoyé par BufferBob Voir le message
    (...) la condition NR == FNR est un trick qui permet de rentrer dans le bloc uniquement si le fichier lu est le premier fichier passé en paramètre (donc fic1.csv), à chaque ligne lue on la stocke dans le tableau f1, et on passe à la ligne suivante avec next pour éviter de rentrer dans le second bloc qui lui sert à traiter fic2.csv
    en clair les fichiers en paramètres sont déroulés ligne par ligne l'un après l'autre dans l'ordre, le premier bloc d'instructions n'est exécuté que lorsqu'on lit le premier fichier grâce à la condition NR == FNR et au next pour ne pas continuer dans le second bloc, tandis que le second bloc n'est exécuté que lorsqu'on lit le second fichier

  16. #16
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    La base de awk c'est de savoir que les champs sont désignés par un $ suivi du rang. $1 est le premier champ. $5, le 5ème champ et $NF le dernier champ puisque NF est le nombre de champs.

    Dans un tableau (peu importe son nom), on stocke les valeurs lorsqu'il parcourt le premier fichier. Ainsi les valeurs sont disponibles lorsqu'il parcourt le second fichier.
    Tu as donc utilisé a[FNR,1] qui est la valeur que tu as mémorisée dans le premier fichier pour le premier champ. Tu avais stocké $1 à l'époque. Mais comme tu le compares avec le premier champ de la ligne en cours de traitement dans le second fichier, c'est à dire $1, autant se simplifier la vie en mettant $1 plutôt que a[FNR,1].

  17. #17
    Membre actif
    Homme Profil pro
    Ingénieur Système Linux
    Inscrit en
    Juillet 2009
    Messages
    24
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur Système Linux

    Informations forums :
    Inscription : Juillet 2009
    Messages : 24
    Par défaut La Ferrari Awk...
    Merci à tous pour vos explications éclairées et éclairantes !

    Cela m'a beaucoup aidé et donne envie d'aller plus loin avec ce langage diablement efficace...

    @+
    Lucky

  18. #18
    Expert confirmé
    Homme Profil pro
    Développeur informatique en retraite
    Inscrit en
    Avril 2008
    Messages
    2 103
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côtes d'Armor (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique en retraite

    Informations forums :
    Inscription : Avril 2008
    Messages : 2 103
    Par défaut
    Citation Envoyé par luckythrice Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (s!="") printf "Identifiant "a[FNR,1]" (ligne %i) différent à la colonne %s.\n",FNR,s;
    Par contre impossible de comprendre pourquoi si j'enlève %i dans le code, je n'ai pas le même résultat...
    Je n'ai pas bien compris si tu as compris. Dans le doute, il faut retirer aussi l'argument FNR (avec sa virgule) du printf:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (s!="") printf "Identifiant "a[FNR,1]" différent à la colonne %s.\n",s;
    ou bien (sans formattage):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (s!="") print "Identifiant "a[FNR,1]" différent à la colonne "s".";

  19. #19
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    +1 mais ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if (s!="") print "Identifiant "$1" différent à la colonne "s".";

Discussions similaires

  1. [PHP 5.3] Additionner les valeurs de certaines colonnes de deux tableaux
    Par renaud26 dans le forum Langage
    Réponses: 5
    Dernier message: 30/05/2014, 00h31
  2. Associer deux colonnes de deux tableaux html différents
    Par clementdevelop dans le forum Balisage (X)HTML et validation W3C
    Réponses: 4
    Dernier message: 18/05/2013, 20h13
  3. Comparaison des colonnes de deux tables différentes
    Par Chakalaka dans le forum PL/SQL
    Réponses: 11
    Dernier message: 22/11/2011, 18h27
  4. [XL-2003] Comparaison de nombres entre deux tableaux
    Par boxtom dans le forum Macros et VBA Excel
    Réponses: 0
    Dernier message: 19/07/2010, 16h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo