Concatenation lignes

Concatenation lignes - Perl - Programmation

Marsh Posté le 07-12-2015 à 11:08:32    

Bonjour à tous,
 
Je débute en perl et je n'arrive pas à concaténer des lignes.
Je m'explique, j'ai 1 seul fichier texte constitué ainsi :  
 

Code :
  1. ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT|RENNES|Mention Complementaire|Non|N|
  2. ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT|RENNES|Ancienneté dans le poste|3|N|
  3. ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT|RENNES|Ancienneté de service|8|N|
  4. LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Enfants à charge|1|N|
  5. LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Ancienneté dans le poste|1|N|
  6. LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Mention Complementaire|Non|N|
  7. LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Ancienneté de service|7|N|


 
Je cherche à faire un test sur la ligne pour donner le resultat suivant :  
 

Code :
  1. ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT|RENNES|Mention Complementaire|Non|Ancienneté dans le poste|3|Ancienneté de service|8|N|
  2. LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Enfants à charge|1|Ancienneté dans le poste|1|Mention Complementaire|Non|Ancienneté de service|7|N|


 
Pour l'instant, voici ce que j'ai réussis à faire mais qui ne fonctionne pas:  
 

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. open(FICHIER, '< C:\Users\mon_fichier.txt') || die;
  5. while (my $whole_line = <FICHIER> ) {
  6. my ($discipline,$nom,$nom_patro,$prenom,$datenaissance,$grade, $ville, $bareme) = split(/\|/, $whole_line);
  7.               print "$discipline ";
  8.      
  9. }
  10. close FICHIER;


 
Après plusieurs tentative de tests, je n'arrive pas à écrire :
 
 Dans le fichier,  SI disciplineligne1 & nomligne1 & prenomigne1 = disciplineligne2 & nomligne2 & prenomigne2 ALORS...
 
 
Merci d'avance pour votre aide.


Message édité par supermallain le 07-12-2015 à 13:22:46
Reply

Marsh Posté le 07-12-2015 à 11:08:32   

Reply

Marsh Posté le 07-12-2015 à 12:59:31    

Faudrait peut être faire l'effort de donner un fichier de test valide.
LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Ancienneté dans le poste|N|
Alors que ça devrait être
LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Ancienneté dans le poste|1|N|
au vu de ta sortie désirée
 
A+,


---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 07-12-2015 à 13:23:18    

gilou a écrit :

Faudrait peut être faire l'effort de donner un fichier de test valide.
LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Ancienneté dans le poste|N|
Alors que ça devrait être
LET MODERN|LOUVOIS|LOUVOIS|JOSE|21/11/1984|CERT|NANTES|Ancienneté dans le poste|1|N|
au vu de ta sortie désirée
 
A+,


 
Autant pour moi. C'est corrigé.

Reply

Marsh Posté le 07-12-2015 à 14:23:04    

Code :
  1. #!/usr/bin/perl
  2. use Modern::Perl;
  3. use autodie;
  4.  
  5. my $re = qr{(?:[^|]+\|)};
  6. my ($prev_start, $prev_end);
  7. open my $fh, '<', 'C:\Users\mon_fichier.txt';
  8. while (<$fh> ) {
  9.  chop;
  10.  if (/^(?<start>$re{7})(?<mid>$re{2})(?<end>$re)$/) {
  11.    if ($. == 1) {
  12.      print $+{start}, $+{mid};
  13.      ($prev_start, $prev_end) = ($+{start}, $+{end});
  14.    }
  15.    else {
  16.      if ($prev_start eq $+{start}) {
  17.         print $+{mid};
  18.      }
  19.      else {
  20.         print $prev_end, "\n", $+{start}, $+{mid};
  21.         ($prev_start, $prev_end) = ($+{start}, $+{end});
  22.      }
  23.    }
  24.  }
  25. }
  26. close $fh;
  27. if ($prev_end) {
  28.    print $prev_end, "\n";
  29. }


 
La ligne est constituée de 10 suites d'un truc formé de texte suivi d'un |. Je colle un tel truc dans une regexp
my $re = qr{(?:[^|]+\|)};
pour réutilisation sous une forme plus lisible. (?:...) est un non-capturing group, qui ne colle pas automatiquement ce qui matche dans une variable.
Bon, on parse ligne a ligne. Si on a une ligne de la forme attendue:
if (/^(?<start>$re{7})(?<mid>$re{2})(?<end>$re)$/) {
(?<nom>...) est un named-capturing group, qui colle ce qui matche dans une variable de nom $+{nom}
Ici, je mets les 7 premiers trucs (éventuellement communs a plusieurs lignes) dans une variable $+{start}, idem pour le truc final, dans $+{end}, et les 2 trucs variables entre les deux seront collés dans $+{mid}.
Si c'est la première ligne
if ($. == 1) {
On imprime le début et le milieu (pas la fin car il y aura peut être d'autres milieux à ajouter)
print $+{start}, $+{mid};
et on stocke le début et la fin courante
($prev_start, $prev_end) = ($+{start}, $+{end});
Sinon, si c'est pas la première ligne
Si le début est le même que celui de la ligne précédente
if ($prev_start eq $+{start}) {
On imprime le nouveau milieu
print $+{mid};
Sinon, si le début est différent de celui de la ligne précédente
On a une nouvelle ligne donc on imprime la fin de la ligne précédente, puis le début et le milieu de la nouvelle ligne en cours
print $prev_end, "\n", $+{start}, $+{mid};
Et on stocke le nouveau début et la nouvelle fin
($prev_start, $prev_end) = ($+{start}, $+{end});
En fin de fichier, on imprime la fin de la dernière ligne lue, s'il y en a une
if ($prev_end) { print $prev_end, "\n"; }
et voila.
 
Note:
Si on peut avoir des champs vide, et donc un || dans les données, remplacer la regexp
my $re = qr{(?:[^|]+\|)};  
par
my $re = qr{(?:[^|]*\|)};  
 
Il y avait aussi moyen de passer par des arrays, ou plutôt par des hashes, mais pour juste cette fusion de lignes, c'était un peu le coup de l'enclume pour craser une mouche. Sinon, une utilisation de Text::CSV aurait pu être pratique.
ou bien faire un
my @fields = qw(discipline nom nom_patro prenom datenaissance grade ville bareme bareme_val yn);
my %data;
...
@data{@fields} = split /\|/;
qui aurait collé les valeurs lues dans le hash suivant l'ordre des clés prises dans @fields.
C'est un idiome perl bien pratique.
 
A+,


Message édité par gilou le 07-12-2015 à 14:40:34

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 07-12-2015 à 15:47:41    

Un GRAND MERCI ! Trop fort ! ça fonctionne très bien  :bounce:  
 
Pour aller plus loin, j'aimerai savoir ce que je dois modifier pour traiter des lignes avec 19 valeurs comme ceci :  
 

Code :
  1. ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT. CL N|MONTPEL.|Mention Complementaire|Non||O| | | |||||N|


 
Vu que j'ai en effet des valeurs vides, j'ai tenté de modifier comme tu me l'a dis la regex : my $re = qr{(?:[^|]*\|)};
Mais j'ai une erreur en sortie...

Reply

Marsh Posté le 07-12-2015 à 15:59:51    

my $re = qr{(?:[^|]*\|)};
oui,
et il faut modifier
if (/^(?<start>$re{7})(?<mid>$re{2})(?<end>$re)$/) {
en fonction de ce qui sera commun ou non.
A vue de nez, ce pourrait être
if (/^(?<start>$re{7})(?<mid>$re{2})(?<end>$re{10})$/) {
mais je ne connais pas vos données.
Ici, j'ai supposé que les 10 derniers champs étaient eux aussi communs.
Je suppose que vous n'avez pas des lignes à 10 et 19 champs dans le même fichier.
 
A+,


Message édité par gilou le 07-12-2015 à 16:02:12

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Marsh Posté le 07-12-2015 à 16:22:32    

C'est ce que j'ai fais aussi pour : if (/^(?<start>$re{7})(?<mid>$re{2})(?<end>$re{10})$/) {
 
J'ai testé de ça me renvoie le résultat suivant :  
 

Code :
  1. ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT|RENNES|Mention Complementaire|Non|Ancienneté dans le poste|3||O| | | |||||N|


il manque :  
 

Code :
  1. Ancienneté de service|8|

après "|3|".
 
Apres la valeur "|O|", les données sont différentes, la valeur entre 2 "|" peut être vide ou pas...  
Toujours est-il qu'on aura toujours 19 valeurs entre les "|".

Reply

Marsh Posté le 07-12-2015 à 20:04:55    

Moi, a partir de vos données, j'ai fait le fichier de test suivant:

ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT. CL N|MONTPEL.|Mention Complementaire|Non||O| | | |||||N|
ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT. CL N|MONTPEL.|Ancienneté dans le poste|3||O| | | |||||N|
ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT. CL N|MONTPEL.|Ancienneté de service|8||O| | | |||||N|


et le code que je vous ai donné,  

Code :
  1. #!/usr/bin/perl
  2. use Modern::Perl;
  3. use autodie;
  4.  
  5. open my $fh, '<', 'ccl.txt';
  6. my $re = qr{(?:[^|]*\|)};
  7. my ($prev_start, $prev_end);
  8. while (<$fh> ) {
  9.  chop;
  10.  if (/^(?<start>$re{7})(?<mid>$re{2})(?<end>$re{10})$/) {
  11.    if ($. == 1) {
  12.      print $+{start}, $+{mid};
  13.      ($prev_start, $prev_end) = ($+{start}, $+{end});
  14.    }
  15.    else {
  16.      if ($prev_start eq $+{start}) {
  17.     print $+{mid};
  18.      }
  19.      else {
  20.     print $prev_end, "\n", $+{start}, $+{mid};
  21.     ($prev_start, $prev_end) = ($+{start}, $+{end});
  22.      }
  23.    }
  24.  }
  25. }
  26. print $prev_end, "\n";
  27. close $fh;


il me donne ceci en sortie:

ANGLAIS|BOBIN|BOBIN|JEAN|28/10/1966|CERT. CL N|MONTPEL.|Mention Complementaire|Non|Ancienneté dans le poste|3|Ancienneté de service|8||O| | | |||||N|

[:souk]  
 
EDIT: Je subodore que votre 3e ligne n'avait pas 19 champs, et donc qu'elle n'a pas été prise en compte.
 
A+,


Message édité par gilou le 07-12-2015 à 20:08:52

---------------
There's more than what can be linked! --    Iyashikei Anime Forever!    --  AngularJS c'est un framework d'engulé!  --
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed