[Résolu][Perl] Découper un fichier en plusieurs et optimisation

Découper un fichier en plusieurs et optimisation [Résolu][Perl] - Perl - Programmation

Marsh Posté le 10-05-2013 à 12:14:57    

Bonjour,
 
J'ai un besoin simple,  
J'ai un fichier source toto.csv qui fait 18M et contient 1,1 millions de lignes.
Je souhaite le découper pour en faire autant de fichier de 100 000 lignes que nécessaire.
 
Pour cela j'ai réalisé un script qui fonctionne, mais le problème c'est qu'il met plusieurs heures !
Ce qui est complètement dingue.
 
Mon besoin initial était d'importer toto.csv en base MySQL dans une simple table sans aucun index, juste une clé primaire, mais cela met ~70mn (avec un load data infile qui plus est....je m'arrache aussi les cheveux pour comprendre pourquoi).
Alors je me suis dis que faire de même mais sur des fichiers plus petit me ferait gagner du temps et bien là c'est perdu.
 
Donc voici à quoi ressemble mon fichier source:

Code :
  1. 1002821311;TAPIS
  2. 1002166671;CHAISE
  3. 1002759082;CHAISE
  4. 1002558833;TABLE
  5. 1002318880;CHAISE
  6. 1002259114;TABLE
  7. 1002805492;CHAISE
  8. 1002074071;CHAISE
  9. 1002548136;CHAISE
  10. 1002884119;TAPIS


 
Voici mon code:

Code :
  1. #!/usr/bin/perl -w
  2. use warnings;
  3. use strict;
  4. # Global Variable
  5. my $DIR="/home/toto/tmp/";
  6. my $FILENAME = "toto.csv";
  7. my $OUT = $DIR . sprintf($FILENAME);
  8. my $maxline = 100000;
  9. my $size = $maxline;
  10. my $output = "file";
  11. my $num = 1;
  12. # Script
  13. open(TOP, "<", $OUT);
  14. while(<TOP> ) {
  15.         chomp;
  16.         if($. != $maxline) {
  17.                 open(FILE, ">>", $DIR.$output.$num);
  18.                 print FILE $., "\t", $_, "\n";
  19.                 close(FILE);
  20.         }
  21.         else {
  22.                 open(FILE, ">>", $DIR.$output.$num);
  23.                 print FILE $., "\t", $_, "\n";
  24.                 close(FILE);
  25.                 $maxline=$maxline+$size;
  26.                 $num++;
  27.         }
  28. }
  29. close(TOP);
  30. __END__


Message édité par Sethenssen le 10-05-2013 à 18:58:04
Reply

Marsh Posté le 10-05-2013 à 12:14:57   

Reply

Marsh Posté le 10-05-2013 à 16:24:53    

C'est très très simple, la lenteur, et ça sera pareil avec tout programme ainsi codé, que ce soit en Perl ou autre chose.  
Tu fais un open(File) et un close(File) (sans compter une opération de positionnement en fin de fichier) qui sont des opérations lentes une fois par ligne du fichier source, soit 2.2 Million d'opérations de ce type... [:bastian:3]  
A 1/100e de seconde par opération, on serait a plus de 6h
 
Un script de ce type devrait faire l'affaire:

Code :
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use autodie;
  5.  
  6. # Global Variable
  7. my $DIR = '/home/toto/tmp/';
  8. my $FILENAME = 'toto.csv';
  9. my $maxline = 100000;
  10.  
  11. #
  12. my $outname = "file";
  13. my $fnum = 1;
  14. open(my $fout, ">", $DIR.$outname.sprintf("%03d", $fnum++));
  15.  
  16. # Script
  17. open(my $fin, "<", $DIR.$FILENAME);
  18. while (<$fin> ) {
  19.  print $fout $_;
  20.  if ($. % $maxline == 0) {
  21.    close($fout);
  22.    open($fout, ">", $DIR.$outname.sprintf("%03d", $fnum++));
  23.  }
  24. }
  25. close($fout);
  26. close($fin);
  27.  
  28. __END__


Le sprintf("%03d", $fnum++) c'est pour avoir des fichiers file001 file002 etc  
 
A+,


Message édité par gilou le 10-05-2013 à 18:46:54

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

Marsh Posté le 10-05-2013 à 18:57:35    

Parfait, merci gilou ! Je passe de plusieurs heures à 2 sec...effectivement la mémoire en prenait un gros coup derrière la casquette.
 
You are the best.

Reply

Marsh Posté le 16-01-2014 à 11:06:57    

Bonjour,
 
Le programme fourni par Gilou (lors de la discussion  "Découper un fichier en plusieurs et optimisation" ) m'est particulièrement utile.
Cependant je cherche à le modifier quelque peu...
 
1) Je ne comprends pas les sens de "$. %" sur cette ligne :  

Code :
  1. if ($. % $maxline == 0)

.
 
2) J'essaie de modifier ce programme, pour que la découpe du fichier se fasse selon le contenu de la ligne (et non à la nième ligne).
Je souhaite découper le fichier quand la ligne commence par dièse, sachant que ce qui suit le dièse serait le nom à donner au fichier.
De type :
 

Code :
  1. # Global Variable
  2. my $FILENAME = 'test.txt';
  3.  
  4. open(my $fout, ">", $rep.$outname);
  5. $outname = "";
  6.  
  7. # Script
  8. open(my $fin, "<", $rep.$FILENAME);
  9. while (<$fin> ) {
  10.  
  11. if ($_ =~ /^#/) {
  12.   close($fout);
  13.   $outname = $_;
  14. $outname =~ s/#//;
  15.   #open($fout, ">", $rep.$outname.sprintf("%03d" . ".aa", $fnum++));
  16.   open($fout, ">", $rep.$outname);
  17. }
  18. else {
  19. print $fout $_;
  20. }
  21. }
  22. close($fout);
  23. close($fin);


 
ça ne fonctionne bien évidemment pas!
Je parviens à découper mon fichier si la ligne commence par "#", mais les nouveaux fichiers ne se nomme pas comme je le souhaite.
 
Quelqu'un peut-il me dire ce qui cloche.
 
D'avance merci,
 
Frelinf
 
La suite ici: http://forum.hardware.fr/hfr/Progr [...] 1182_1.htm


Message édité par gilou le 21-01-2014 à 15:11:47
Reply

Sujets relatifs:

Leave a Replay

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