trier un fichier texte

trier un fichier texte - Perl - Programmation

Marsh Posté le 15-02-2012 à 15:56:36    

Bonjour,
 
Voilà je cherche à trier un fichier texte et je bloque sur la méthode split où l'erreur m'indique que celle-ci n'est pas initialisé. J'ai effectivement oublier de l'initialiser j'ai donc suivi à la lettre mais la même erreur revient :
 

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. system("rrdtool fetch /var/lib/centreon/metrics/50.rrd AVERAGE --start \$\(date -d\'01 january 2012' +%s ) --end \$\( date -d\'31 January 2012\' +%s) > 50.txt" );
  5. #-------Ouverture du fichier contenant les données renvoyées par rrdfetch()
  6. my $cpt = 0;
  7. my @tabtab =();
  8. my @tabtab2 = ();
  9. my $linelement;
  10. my $word = "nan";
  11. open(FF,"50.txt" );
  12.         while(my $line = <FF> )
  13.          {
  14. #si la ligne que je parcours ne comporte pas ce mot je l'ajoute
  15. # "i" permettant de ne pas faire de distinction entre majuscule et minuscule
  16.                  if ($line !~ /$word/i)
  17.                 {
  18.                  push(@tabtab,$line);
  19. $cpt = $cpt + 1;
  20.                 }
  21.          }
  22. close FF;
  23. print $cpt;
  24. foreach $linelement(@tabtab) {
  25.        my ($datetimestamp, $data) = split( ":", $linelement);
  26.         open(FILE,">test.txt" );
  27. #       print $data;
  28.         print FILE $data;
  29.         close FILE;
  30. }


 
 
 
Le fichier texte est composé d'a peu prés 4000 lignes de cette forme :
 
1328051100: 5.5884907200e+06
 
La partie gauche c'est du timestamp, c'est l'autre partie qui m'intéresse, le fichier texte comportait des lignes avec un mot clé qui indiquait qu'elle ne comportait pas de valeur, j'ai donc copié uniquement celle qui comportaient une valeur dans mon tableau.  
Ensuite je cherche a séparé ces 2 valeurs par la méthode split mais c'est là que survient le problème.
En faite, je cherche a écrire uniquement la partie $data qui contient 5.5884907200e+06 (par exemple), dans un autre fichier texte.
Pour au final faire une moyenne de toutes ces valeurs contenu dans $data arrondie à 10^-2 .


Message édité par furil le 15-02-2012 à 16:14:30
Reply

Marsh Posté le 15-02-2012 à 15:56:36   

Reply

Marsh Posté le 15-02-2012 à 17:01:57    

Pour ta moyenne, ceci devrait coller:

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4.  
  5. system("rrdtool fetch /var/lib/centreon/metrics/50.rrd AVERAGE --start \$\(date -d\'01 january 2012' +%s ) --end \$\( date -d\'31 January 2012\' +%s) > 50.txt" );
  6. #-------Ouverture du fichier contenant les données renvoyées par rrdfetch()
  7.  
  8. my $word = "nan";
  9. my $num = 0;
  10. my $cpt = 0;
  11.  
  12. open(FF,"<50.txt" );
  13. map {s/\s*\n$//;; s/^\d+:\s*//; if ($_) {$num += $_; ++$cpt;}} grep !/$word/io, <FF>;
  14. close FF;
  15.  
  16.  
  17. if ($cpt) {
  18.  printf "%e\n", $num/$cpt;
  19. }
  20. else {
  21.  printf "Aucune ligne trouvee\n"
  22. }

Testé sur un fichier 50.txt comme ceci:

1328051100: 5.5884907200e+06  
1328051100: 5.5894907200e+06  
1328051100: nan
1328051100: 5.5874907200e+06  
1328051100: nan  
1328051100: 5.5864907200e+06  
 

C:\Perl>perl test.pl
5.587991e+006


Je suis absent pour 1 à 2h, mais je mettrais qques explications ensuite si nécessaire.
 
A+,


Message édité par gilou le 15-02-2012 à 17:04:04

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

Marsh Posté le 15-02-2012 à 18:05:20    

map {s/\s*\n$//;; s/^\d+:\s*//; if ($_) {$num += $_; ++$cpt;}} grep !/$word/io, <FF>;


1) grep !/$word/io, <FF>;
ça fait un grep sur le fichier et retourne les lignes ne contenant pas $word. ça retourne ces lignes comme un array
2) map {s/\s*\n$//;; s/^\d+:\s*//; if ($_) {$num += $_; ++$cpt;}} (@lignes)
ça applique {s/\s*\n$//;; s/^\d+:\s*//; if ($_) {$num += $_; ++$cpt;}} à chaque ligne
3) {{s/\s*\n$//;; s/^\d+:\s*//; if ($_) {$num += $_; ++$cpt;}}if ($_) {$num += $_; ++$cpt;}}
Ça vire ce qui est inutile a chaque extrémité: s/\s*\n$//;; s/^\d+:\s*//;
s'il reste qque chose dans la ligne courante $_, ça doit être la chaine contenant les caractères du nombre (et rien de plus), perl le convertit directement en un nombre si je l'utilise en contexte numérique (avec une addition ici).  
Donc si j'ai trouvé un nombre restant a ajouter à ma moyenne, je l'ajoute, et j'augmente le nombre de nombres ajoutés à la moyenne: if ($_) {$num += $_; ++$cpt;}
 
A+,


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

Marsh Posté le 16-02-2012 à 10:27:50    

Bonjour Gilou,
 
Encore merci, j'avance plus vite grâce à toi.  :)  
 
Ensuite au niveau de l'ensemble j'ai compris ce que fait le script mais :
 

Code :
  1. {s/\s*\n$//;; s/^\d+:\s*//;


 
J'ai compris l'explication générale, j'ai même refait quelques script avec cette fonction en utilisant \w+ (...)
Mais serait-il possible que tu me détaille cette ligne  car j'avoue que si on me demande d'expliquer au détail, je ne saurais pas comment m'y prendre  :ange:  
 
Enfin le résultat est certes bon mais je n'arrive pas à le convertir en décimal, plus précisement à deux chiffres aprés la virgule avec %0.2f j'obtiens le résultat suivant :
 
5261226.49 alors que je veux 5.26
 
Merci d'avance et bonne journée


Message édité par furil le 16-02-2012 à 11:20:10
n°2126792
gilou
Modérateur
Modzilla
Posté le 16-02-2012 à 11:46:58  profilanswer
 

{s/\s*\n$//; s/^\d+:\s*//; (il y avait un ; inutile après un premier ;)
c'est  
1) s/\s*\n$//;  
Un classique: on nettoie la fin de ligne
On vire les espaces éventuels (\s espace \s* espaces éventuels) suivis d'un caractère de fin de ligne (\n) en fin de ligne ($)
s/toto// ça remplace (une fois) toto par rien, donc ça le vire, dans la variable $_ qui contient la ligne courante
car, map { blabla } @toto; c'est la même chose que foreach (@toto) {blabla;} qui assigne chaque ligne du tableau @toto à $_
L'avantage de map que j'utilise ici, c'est que comme le tableau arrive après le bloc, on peut y substituer un truc complexe en fin de ligne, alors que ça serait lourd avec un foreach.
2) s/^\d+:\s*//
la aussi, clairement c'est du nettoyage, puisqu'on a // en fin d'expression:
on vire ce qui est en début de ligne (^), qui est constitué d'une suite de chiffres suivis d'un : (\d+:) suivi éventuellement de blancs (\s*)
Bref cette partie vire ce qui est avant le 5.5884907200e+06 d'une ligne 1328051100: 5.5884907200e+06  
tandis que l'autre vire ce qui est après le 5.5884907200e+06 d'une ligne 1328051100: 5.5884907200e+06    (on le voit pas mais il y a du blanc)
Donc au final, les deux ne laissent que la partie 5.5884907200e+06 d'une ligne 1328051100: 5.5884907200e+06  et c'est ce qu'il faut à perl pour qu'il puisse l'interpréter comme un nombre (du blanc avant ou après ferait échouer cela).
 
A+,


Message édité par gilou le 16-02-2012 à 12:01:13

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

Marsh Posté le 16-02-2012 à 11:59:54    

Rapide, clair, efficace. :D
 
Merci j'ai compris le principe et pour le ";" je me disais aussi  :lol:  
 
Bonne journée.

Reply

Marsh Posté le 16-02-2012 à 12:01:24    

Citation :

Enfin le résultat est certes bon mais je n'arrive pas à le convertir en décimal, plus précisement à deux chiffres aprés la virgule avec %0.2f j'obtiens le résultat suivant :  
 5261226.49 alors que je veux 5.26

Comme tu as pu voir sur mon exemple, c'est %.2e et non pas %.2f qu'il faut utiliser
Par contre on ne peut pas contrôler avec (s)printf le nombre de chiffres de l'exposant (2 ou 3).
Bon, il y a toujours moyen de mettre ça dans une chaîne sur laquelle on fait s/\+00/+0/ avant d'afficher la chaine.
  my $a = sprintf "%0.2e\n", $num/$cpt;
  $a =~ s/\+00/+0/;
  print $a;
 
A+,


Message édité par gilou le 16-02-2012 à 12:11:40

---------------
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

© 2018 Forum. All Rights Reserved.