Comparer plusieurs lignes

Résolu
papavers Messages postés 24 Date d'inscription   Statut Membre Dernière intervention   -  
[Dal] Messages postés 6205 Date d'inscription   Statut Contributeur Dernière intervention   -
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 18789 Date d'inscription   Statut Contributeur Dernière intervention   5 637
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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   Statut Membre Dernière intervention   1
 
Bonjour, aucune idée pour le moment :-(
0
[Dal] Messages postés 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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   Statut Membre Dernière intervention   1
 
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 18789 Date d'inscription   Statut Contributeur Dernière intervention   5 637
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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 18789 Date d'inscription   Statut Contributeur Dernière intervention   5 637
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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   Statut Membre Dernière intervention   1
 
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 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
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