Comparer plusieurs lignes

Résolu/Fermé
papavers Messages postés 24 Date d'inscription mardi 6 mai 2008 Statut Membre Dernière intervention 10 mai 2012 - 9 mai 2012 à 17:50
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 - 10 mai 2012 à 18:58
Bonjour à tous,

Apres plusieurs heures de casse tête je m'en remet à vous.

J'ai un fichier texte à 2 colonnes. Je dois comparer les valeurs de la premiere colonne, et partout où les valeurs sont identiques, j'affiche le résultat sur une ligne.

exemple.

Input file

7051060508 8050842880
7051060508 7051038719
7052991542 8056725494
7052991542 8070996945
7052991542 8073010138
7052991577 8159484404
7052991600 8055099690
7052991600 8050277010

Output file

7051060508 8050842880 7051038719
7052991542 8056725494 8070996945 8073010138
7052991577 8159484404
7052991600 8055099690 8050277010

J'espère avoir été aussi clair que possible.

Merci déjà pour votre aide!!

4 réponses

dubcek Messages postés 18718 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 22 mars 2024 5 615
10 mai 2012 à 15:22
avec doublons et lignes non triées
$ cat fichier
7051060508 8050842880
7051060508 7051038719
7052991542 8056725494
7052991542 8070996945
7052991542 8073010138
7051060508 8050842880
$ 
$ awk '{xx[$1, $2]++} !x[$1] {x[$1]=$1}  xx[$1 ,$2]<2 {x[$1]=x[$1] FS $2}END{for (n in x)print x[n] }' fichier
7052991542 8056725494 8070996945 8073010138
7051060508 8050842880 7051038719
$
2
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
10 mai 2012 à 17:52
Bravo ! C'est bien mieux d'utiliser le tableau associatif x pour stocker des valeurs et de ne les afficher qu'à la fin... et pas d'espace qui traîne en fin de ligne :-)
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
10 mai 2012 à 18:58
Pour le fun, en Perl aussi on peut faire des "one-liners" peu compréhensibles. On peut "résumer" mon script comme cela :

perl -pe 'chomp;@c=split(/ /);$_="";if($h{$c[0]}!~/$c[1]/){$h{$c[0]}.= $c[1]." ";}END{for$k(keys%h){chop($s="$k $h{$k}");print"$s\n";}}' < fichier

je suis sûr qu'un spécialiste de Perl (que je ne suis pas) arriverait à faire plus court et moins compréhensible ;-P
0
papavers Messages postés 24 Date d'inscription mardi 6 mai 2008 Statut Membre Dernière intervention 10 mai 2012 1
9 mai 2012 à 18:45
Bonjour, aucune idée pour le moment :-(
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
9 mai 2012 à 19:09
Salut papavers,

C'est assez simple avec Perl.

Tu traites chaque ligne avec un split sur l'espace, en utilisant la 1ère colonne comme clef d'un hash, et en y affectant le contenu de la 2ème colonne comme valeur du hash concaténée à la valeur existante (avec un espace).

Puis tu affiches le contenu du hash.


Dal
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
9 mai 2012 à 19:26
Tu peux faire comme cela :

#!/usr/bin/perl

use strict;
use warnings;

my $ligne;
my %hash;

open(FICH, "/root/Documents/testperl/fichiertexte.txt") || die "Impossible d'ouvrir fichiertexte.txt: $!\n";
foreach $ligne (<FICH>) {
  chomp($ligne);
  my @colonnes = split(/ /, $ligne);
  $hash{$colonnes[0]} .= $colonnes[1] . " ";
}

my $clef;

foreach $clef (sort keys %hash) {
  print "$clef $hash{$clef}\n";
}
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié par [Dal] le 9/05/2012 à 19:34
si cela t'ennuie d'avoir un espace terminal, tu peux le supprimer avant de l'afficher.

ta boucle foreach d'affichage peut alors ressembler à cela :

foreach $clef (sort keys %hash) { 
  $_ = "$clef $hash{$clef}"; 
  chop; 
  print $_ ."\n"; 
}
0
papavers Messages postés 24 Date d'inscription mardi 6 mai 2008 Statut Membre Dernière intervention 10 mai 2012 1
9 mai 2012 à 23:23
Mr Dal, tu me sauve la "vie"! J'avoue que j'utilise pas vraiment perl, et je nai pas pu trouver la bonne combinaison avec awk.
mAis là j'ai bien pris le temps de comprendre ton script.
Encore merci
0
dubcek Messages postés 18718 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 22 mars 2024 5 615
10 mai 2012 à 08:27
hello
avec awk
$ awk '!x[$1] {if (n++)print "" ; x[$1]=$1 ; printf $1 FS} {printf $2 FS}END{print ""}' fichier
7051060508 8050842880 7051038719 
7052991542 8056725494 8070996945 8073010138 
7052991577 8159484404 
7052991600 8055099690 8050277010 
$ 
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
10 mai 2012 à 10:03
Salut dubcek,

Avec un shell csh sous FreeBSD et avec le awk de Brian Kernighan (pas le gawk que l'on trouve sous Linux), ta ligne donne "x[: Event not found.". Elle fonctionne en revanche sur la même machine avec un shell sh et bash avec le même awk.

Dal
0
dubcek Messages postés 18718 Date d'inscription lundi 15 janvier 2007 Statut Contributeur Dernière intervention 22 mars 2024 5 615
10 mai 2012 à 10:19
j'ai le même message avec tcsh, essayer
awk '\!x[$1] {if (n++)print "" ; x[$1]=$1 ; printf $1 FS} {printf $2 FS}END{print ""}' fichier
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié par [Dal] le 10/05/2012 à 11:28
Oui, comme cela la commande fonctionne sous csh (plus sous sh ou bash, où une autre erreur s'affiche).

Il me semble que ton algorithme suppose que les lignes dans le "fichier" sont dans l'ordre pour que les résultats soient exacts. Pour parer au cas où elles sont dans le désordre, je la précèderai aussi d'un sort qui "pipe" les données vers awk. Comme cela, avec bash :

sort fichier | awk '!x[$1] {if (n++)print "" ; x[$1]=$1 ; printf $1 FS} {printf $2 FS}END{print ""}'

Par contre, elle laisse un espace en fin de chaque lignes affichées, mais mes connaissance sont limitées en awk, et je ne vois pas comment faire pour supprimer le dernier espace. Je ne sais pas si c'est vraiment gênant pour papavers.

Je suis toujours épaté de ce que l'on peut faire avec awk, je ne savais pas qu'il gérait les tableaux associatifs. C'est un peu tarabiscoté, mais malin et compact !
0
papavers Messages postés 24 Date d'inscription mardi 6 mai 2008 Statut Membre Dernière intervention 10 mai 2012 1
10 mai 2012 à 11:54
Hello Dubcek, c'etait l'astuce que je recherchais avec awk. La premiere syntaxe fonctionne à mon niveau sous ksh.
Et côté performance même si on a des lignes identiques le resultat est pareil, contrairement a la commnde perl.

exemple

7051060508 8050842880
7051060508 7051038719
7052991542 8056725494
7052991542 8070996945
7052991542 8073010138
7051060508 8050842880

AWK
7051060508 8050842880 7051038719
7052991542 8056725494 8070996945 8073010138 8050842880

PERL
7051060508 8050842880 7051038719 8050842880
7052991542 8056725494 8070996945 8073010138
0
[Dal] Messages postés 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié par [Dal] le 10/05/2012 à 12:56
Non, dans ton nouvel exemple l'algorithme utilisé dans la commande awk renvoie un résultat faux car la dernière ligne n'est pas triée et le dernier "8050842880" en colonne 2 est compris comme la suite de la série "7052991542" de la colonne 1, et affiché à la fin de la 2ème ligne de résultat, alors sa clef est "7051060508" et non "7052991542".

Pour que la commande awk renvoie des résultats corrects sur un fichier non trié, il faut le trier au préalable, comme indiqué dans mon message ci-dessus.

Si tu fais cela, tu verras que tu auras le même résultat que le script Perl.

Si tu veux éviter les doublons, tu passeras ton fichier par uniq également, après l'avoir filtré avec sort.

Avec le script Perl, le sort préalable n'est pas nécessaire, car l'algorithme proposé se sert du hash (le tableau associatif sous Perl) pour stocker les valeurs correspondant aux clefs. Si tu veux éviter les doublons, tu peux faire un test de la valeur du hash, pour vérifier qu'elle ne contient pas déjà la valeur que l'on ajoute.

if ($hash{$colonnes[0]} !~ /$colonnes[1]/) { $hash{$colonnes[0]} .= $colonnes[1] . " "; }

Mais, donc, en faisant :

sort fichier | uniq | awk '!x[$1] {if (n++)print "" ; x[$1]=$1 ; printf $1 FS} {printf $2 FS}END{print ""}'

Tu devrais obtenir le même résultat, à l'exception des espaces additionnels qui restent en fin de lignes avec la commande awk.

Dal
0