Forum Clubic

Perl et le tri

Bonjour à tous,

Je bosse actuellement sur un script qui permet de convertir un fichier de format A vers un fichier de format B (pour faire simple).
A un moment, je dois trier les lignes du fichier A car c’est nécessaire dans la suite du traitement.
Seulement, je souhaite complexifier un peu le tri.

Un peu d’explication :
le fichier d’entrée contient n lignes, chaque champ (ou valeur peu importe) est séparé par un pipe ‘|’.
la première ligne du fichier contient le nom des colonnes indiquant ce que sont les champs.
exemple :
account|investissement1|adresse1|adresse2|adresse3|pays|codepostal
123456|FundAfrica|Bank Money|34 rue de la mairie|etage 4|France|76500|
98765|FundAfrica|Bank Money|34 rue de la mairie|etage 4|France|76500|

Mon but est de sécuriser le tri en ne triant par sur un numéro de colonne fixe, mais sur un numéro qui aura été calculé dynamiquement en fonction de la position de la colonne cherchée dans le fichier. CAD que si je fais un tri sur la colonne ‘pays’, je chercherais d’abord l’indice du champ ‘pays’ dans la première ligne du fichier. Cela permet, dans le cas d’un ajout de colonne aléatoire de ne pas avoir à modifier le code car ce dernier saura où elle se trouve lors de l’exécution.

Là où ça se corse, c’est que je dois trier suivant 3 colonnes.

J’ai déjà une solution qui fonctionne mais le temps de traitement est très long, environ 12s pour 4 Mo de données.


my @indexcolonne = getIndexColonne( 'account' ,'adresse1' , 'pays' , @tabIn );
my @sorttab = sort {
(split /\|/ , $a)[indexcolonne[0]] cmp (split /\|/ ,$b)[indexcolonne[0]] || 
(split /\|/ ,$a)[indexcolonne[1]] cmp (split /\|/ ,$b)[indexcolonne[1]] || 
(split /\|/ ,$a)[indexcolonne[2]] cmp (split /\|/ , $b)[indexcolonne[2]] 
} @tabIn;

une autre solution est d’utiliser le sort BLOCK avec l’appel à une sous fonction, en ayant auparavant splitté chaque ligne du tableau tabIn de sorte à avoir une matrice. Cette méthode est beaucoup plus rapide ( 1,5seconde en gros) mais il reste toujours les 3 ‘cmp’.
Et c’est ce que je veux éviter. Je n’aime pas écrire en dur indexcolonne[qqch] dans le code, je voudrais rendre celà dynamique. Par exemple :


sub sortData{ 
 my (@a , @b) = @_;
 //il doit manqueer une déreference..., mais bref

 my $res = 0;
 foreach (@indexcolonne){
  $res = $res || ($a[$_] cmp $b[$_]);
 }
return $rex;
}

my @indexcolonne; #variable globale, doit pouvoir être passé en paramètre
main{
...
my @sorttab = sort sortData @tabIn;
...}

mon problème est sur la variable $res et le traitment du résultat ‘cmp’ qui sont faux car le tri est mal réalisé.

J’aurais donc voulu savoir si vous avez des suggestions de corrections, voire des algos pour résoudre ce problème tout en gardant à l’esprit que la position des colonnes et le nombre de colonnes utilisées pour le tri peuvent être variables.

Je ne connais pas Perl, mais de ce que je comprend ce ce qui est fait, pour chaque ligne tu resplitte les lignes lues.

Si les fichiers sont petits (ou plutôt d’une taille raisonnable ne dépassant pas les 100MiB), tu as peut-être plus vite fait de lire tes fichiers et les stocker en mémoire sous forme de tableau indexé, plutôt que de faire le tri par ligne, en splittant chaque ligne en colonne (sans compter les erreurs que tu peux si la ligne a moins de colonnes que prévues).

Ensuite, à supposer que tu ai tes données sous forme de tableau, tu peux utiliser un tableau d’index de colonne, et dans ta fonction de tri :

function tri($taba, $tabb) {
  $n = 0;
  for ($i in $colindex) {
    $n = cmp($taba[$i], $tabb[$i]);
    if ($n != 0) break;
  }
  return $n;
}

J’ai écrit ça en PHP, mais bon tu sauras transformer :stuck_out_tongue:

merci de ton aide, non seulement ça m’a aiguillé sur la bonne voie, mais ça m’a aussi permis de corriger mes erreurs (de dev).

pour expliquer la solution :
il faut bien surcharger la fonction sort :

my @tabSort = sort { sortComplex($a , $b) } @tabIn

et l’écriture de la fonction donne qqch du genre :

sub sortComplex{ 
 my ($a , $b) = @_;
 #dereference ok

 my $res = 0;
 foreach (@indexcolonne){
  $res = ($a[$_] cmp $b[$_]);
  if( $res != 0){ return $res;}
  }
 return $res
 }
return $rex;
}

j’ajouterai qu’on peut rajouter une référence dans l’appel de la fonction ‘sortComplex’ pour le tableau @indexcolonne
Dans mon programme, je l’ai déclaré en variable globale, ce qui n’est pas le mieux à faire.

je tacherai de corriger le code fourni lundi. :jap: