[PERL] aide script pour alimenter SGBD MySQL

aide script pour alimenter SGBD MySQL [PERL] - Perl - Programmation

Marsh Posté le 10-11-2009 à 03:00:54    

Bonjour,
 
Je débute en Perl et je suis en train de lire des doc pour m'initier. Cependant je désespère et je ne sais pas si j'aurai le courrage de terminer.
 
Ma problématique est simple, j'ai de multiples fichiers que je dois simplement manipuler, mettre en forme pour alimenter ma base de donnée MySQL.
J'utilise MySQL 5.1, Toad v4.1 & Perl v5.10
 
Si un dieu vivant du perl passe dans le coin je lui en serais reconnaissant !
 
Voilà un exemple parmi tant d'autre d'un fichier csv:
 

Code :
  1. 245362;77;77289;INR69907;Feutre;799;20091102232406;;VEL;799;66;24;
  2. 243640;77;66017;INT81553;"Cahier";699;20091102232318;;VEL;699;66;24;
  3. 236958;77;08;DRT03286;Lit;299;20091102231945;;VEL;299;66;15;
  4. 319771;77;79060;INA38898;Stylo;399;20091102231902;;VEL;399;66;3;
  5. 319771;77;79060;BTQ38898;Stylo;399;20091102231902;;VEL;399;66;24;
  6. 319250;77;13110;INJ40768;Matelas;599;20091102231736;;VEL;599;66;24;
  7. 320343;77;21758;PRP31628;Souris;299;20091102231459;;VEL;299;66;24;
  8. 331379;77;27209;INT59377;Trousse;299;20091102231319;;VEL;299;66;15;
  9. 314147;77;89301;BTQ12922;Ciseau;0;20091102231248;;VEL;0;66;24;
  10. 334467;77;26234;TEL38873;Mouchoir;499;20091102231131;;VEL;499;66;24;
  11. 241749;77;37732;TEL90191;Effaceur;699;20091102231104;;VEL;699;66;24;


 
Le résultat souhaité:

Code :
  1. insert into vod_billing values("236958","8","DRT03286","Lit","299","20091102231945","15","DRT" );
  2. insert into vod_billing values("241749","37732","TEL90191","Effaceur","699","20091102231104","24","TEL" );
  3. insert into vod_billing values("243640","66017","INT81553","Cahier","699","20091102232318","24","INT" );
  4. insert into vod_billing values("245362","77289","INR69907","Feutre","799","20091102232406","24","INT" );
  5. insert into vod_billing values("314147","89301","BTQ12922","Ciseau","0","20091102231248","24","BTQ" );
  6. insert into vod_billing values("319250","13110","INJ40768","Matelas","599","20091102231736","24","INT" );
  7. insert into vod_billing values("319771","79060","BTQ38898","Stylo","399","20091102231902","24","BTQ" );
  8. insert into vod_billing values("319771","79060","INA38898","Stylo","399","20091102231902","3","INT" );
  9. insert into vod_billing values("320343","21758","PRP31628","Souris","299","20091102231459","24","PRP" );
  10. insert into vod_billing values("331379","27209","INT59377","Trousse","299","20091102231319","15","INT" );
  11. insert into vod_billing values("334467","26234","TEL38873","Mouchoir","499","20091102231131","24","TEL" );


 
En français cela donne ça:
1.concaténer tous les fichiers *.csv* d'un répertoire en un seul et même fichier
2.remplacer tous les caractères " par rien (la double quote)
3.Supprimer toutes les lignes en doublons
4.Trier par la première colonne
5.De supprimer toutes les lignes vides
6.Le caractère ; est le délimiteur de colonne alors supprimer la colonne 2,8,9,10 & 11
7.D'ajouter en dernière colonne les 3 premiers caractères de la colonne 3 (TEL, PRP etc...)
8.Sur cette dernière colonne, remplacer tous les INA, INJ & INR par INT
9.Insérer en début de ligne insert into vod_billing values("
10.D'insérer en fin de ligne " );
11.De remplacer le délimiteur ; par ","
12.Renommer le fichier de sorti en .sql
 
Le but ultime serait d'alimenter directement ma base MySQL avec des lignes crées, mais déjà un tel script serait le nirvana.
 
Je sais c'est un peu abusé mais sans doute simple pour un developpeur !


Message édité par Sethenssen le 10-11-2009 à 03:05:14
Reply

Marsh Posté le 10-11-2009 à 03:00:54   

Reply

Marsh Posté le 10-11-2009 à 11:10:07    

y'a une différence entre aider quelqu'un qui débute, et lui filer les scripts tout fait
 
pour t'aiguiller, je te propose d'aller lire un peu l'aide sur les fonctions open(), split(), close()
Un petit coup de foreach() t'aidera aussi
 
Ensuite tu postes ton code et on t'aide

Reply

Marsh Posté le 10-11-2009 à 11:12:29    

:hello:  
Tout ça c'est très facile en perl, mais quelle serait (un ordre de grandeur) la taille (en nombre de lignes) d'un fichier .csv obtenu par concaténation de ceux figurant dans un répertoire, ceci afin de voir la stratégie la plus adaptée (tout en mémoire ou non) pour supprimer les doublons et faire le tri.
A+,


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

Marsh Posté le 10-11-2009 à 11:21:24    

Citation :

En français cela donne ça:
1.concaténer tous les fichiers *.csv* d'un répertoire en un seul et même fichier
2.remplacer tous les caractères " par rien (la double quote)
3.Supprimer toutes les lignes en doublons
4.Trier par la première colonne
5.De supprimer toutes les lignes vides
6.Le caractère ; est le délimiteur de colonne alors supprimer la colonne 2,8,9,10 & 11

Le bon ordre pour faire cela est:
1. Concaténer tous les fichiers *.csv* d'un répertoire en un seul et même fichier
2. De supprimer toutes les lignes vides
3. Le caractère ; est le délimiteur de colonne alors supprimer la colonne 2,8,9,10 & 11
4. Trier par la première colonne
5. Supprimer toutes les lignes en doublons
6 .Remplacer tous les caractères " par rien (la double quote)
Parce que sinon, on peut avoir des lignes qui ne sont pas doublon avant la suppression de colonnes qui le deviennent ensuite (je ne connais pas exactement la structure des données des lignes, donc c'est supposable) [ou alors l'ordre donné initialement est il important et permet-on des doublons après les suppression de colonnes?]
Supprimer les doublons après tri est optimal: les doublons seront alors successifs, ce qui facilite les choses.
A+,

Message cité 1 fois
Message édité par gilou le 10-11-2009 à 11:39:43

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

Marsh Posté le 11-11-2009 à 04:45:07    

gilou a écrit :

1. Concaténer tous les fichiers *.csv* d'un répertoire en un seul et même fichier
2. De supprimer toutes les lignes vides
3. Le caractère ; est le délimiteur de colonne alors supprimer la colonne 2,8,9,10 & 11
4. Trier par la première colonne
5. Supprimer toutes les lignes en doublons
6 .Remplacer tous les caractères " par rien (la double quote)
7.D'ajouter en dernière colonne les 3 premiers caractères de la colonne 3 (TEL, PRP etc...)  
8.Sur cette dernière colonne, remplacer tous les INA, INJ & INR par INT  


Bonjour,
Comme énoncé j'ai donc commencé à faire le script via l'ordre du quote (j'ai rajouté l'étape 7 & 8)

Code :
  1. #!/usr/bin/perl -w
  2. use strict;
  3. use warnings;
  4.  
  5. # ************************************************************
  6. # Global Variables
  7. my $LOCAL_DIR = "/cygdrive/d/_.billing/_.compiler/backup/test/";
  8. my $DEST_DIR = "/cygdrive/d/_.billing/_.compiler/backup/";
  9. my $SEARCH_FILE = "*_billing_*";
  10. my $OUTPUT_FILE = $DEST_DIR . sprintf("RESULT_FILE.SQL" );
  11. my $START_SEP = 'insert into zz_billing values("';
  12. my $SEP = '","';
  13. my $END_SEP = '" );';
  14.  
  15. # ************************************************************
  16. # Script Perl
  17. print "Starting...\n";
  18. print "\n";
  19. # ************************************************************
  20.  
  21. open (OUT,">$OUTPUT_FILE" ) or die "Message : $!\n";
  22. my @RESULT_FILE = <$LOCAL_DIR$SEARCH_FILE>;
  23. foreach my $LINE(@RESULT_FILE)
  24. {
  25.  open IN,"$LINE" or warn "Message : $!\n";
  26.  while(<IN> )
  27.  {
  28.      s/\"//g;
  29.      next if m/^$/;
  30.      my @SPLIT_TAB = split(/;+/);
  31.      push(@SPLIT_TAB,substr($SPLIT_TAB[3],0,3));
  32.      chomp(@SPLIT_TAB);
  33.      if($SPLIT_TAB[11] eq 'INA')
  34.      {
  35.          my $ERASE_COLUMN = pop(@SPLIT_TAB);
  36.          push(@SPLIT_TAB,'INR');
  37.          chomp(@SPLIT_TAB);
  38.      }
  39.      elsif($SPLIT_TAB[11] eq 'INS')
  40.      {
  41.          my $ERASE_COLUMN = pop(@SPLIT_TAB);
  42.          push(@SPLIT_TAB,'INR');
  43.          chomp(@SPLIT_TAB);
  44.      }
  45.      elsif($SPLIT_TAB[11] eq 'INJ')
  46.      {
  47.          my $ERASE_COLUMN = pop(@SPLIT_TAB);
  48.          push(@SPLIT_TAB,'INR');
  49.          chomp(@SPLIT_TAB);
  50.      }
  51.      print OUT "$START_SEP$SPLIT_TAB[0]$SEP$SPLIT_TAB[2]$SEP$SPLIT_TAB[3]$SEP$SPLIT_TAB[4]$SEP$SPLIT_TAB[5]$SEP$SPLIT_TAB[6]$SEP$SPLIT_TAB[10]$SEP$SPLIT_TAB[11]$END_SEP\n";
  52.      #print OUT $_;
  53.  }
  54. }
  55.  
  56. # ************************************************************
  57. # Ending...
  58. print "\n";
  59. print "Ending...\n";
  60. END
  61. # End.
  62. # ************************************************************


 
J'ai réussi les tâches 1, 2, 6, 7 & 8 et la 3.
 
Pour la tâche 3 la suppression n'est pas faite car je sélectionne que certaines colonnes, mais cela je pense n'est pas optimisé.
 
Il ne me reste que la 4 & 5 qui peuvent être faites en même temps peu importe.
Je pense que je dois le faire dans un second temps, dans un autre fichier car le foreach actuel traite ligne par ligne donc il ne peut pas encore faire de tri s'il n'a pas toutes les données.
Mon problème est que je ne sais pas comment récupérer ce que je viens de faire pour le mettre dans un second tableau et commencer par un sort.
Puis après il faudra faire le tri sans doublons mais là je ne sais pas, j'ai tenté avec un tableau de hashage mais sans succès.
 
Pour répondre aux questions de Gilou:
L'ordre de grandeur est d'environ 30 000 lignes.
Il n'y a pas de restriction de faire la suppression de doublon avant ou après le tri, peu importe.
 
 
Merci pour votre aide,
++


Message édité par Sethenssen le 11-11-2009 à 15:48:39
Reply

Marsh Posté le 11-11-2009 à 17:04:55    

Citation :

Pour répondre aux questions de Gilou:
L'ordre de grandeur est d'environ 30 000 lignes.
Il n'y a pas de restriction de faire la suppression de doublon avant ou après le tri, peu importe.

Donc le tri devrait pouvoir se faire en mémoire sans problème.

 

Bon, Je me suis créé a partir de vos exemples deux fichier .csv et j'ai écrit un petit script qui fait la modification et l'imprime à l'écran, a vous de le comprendre (et/ou poser des questions) et l'adapter a vos besoins (écrire la sortie vers un fichier...)

 
Code :
  1. #!/usr/bin/perl -w
  2. use strict;
  3. use warnings;
  4.  
  5. use IO::File;
  6. use File::Spec; # Pour la portabilité du script quelque soit le système de fichier
  7.  
  8. my $LOCAL_DIR = File::Spec->canonpath('C:\Perl');  #a adapter a vos besoins
  9. my @in_dir = File::Spec->splitpath($LOCAL_DIR, 1);
  10. pop @in_dir;
  11.  
  12. my $in_dirhandle;
  13. opendir($in_dirhandle, $LOCAL_DIR) or die "Ouverture de $LOCAL_DIR impossible: $!";
  14. my @files = grep { -f }
  15.            map  { File::Spec->catpath(@in_dir, $_) }
  16.            grep { /\.csv$/ }    # a remplacer par votre critère de filtrage, _billing_tvod_
  17.            readdir($in_dirhandle);
  18. closedir($in_dirhandle);
  19.  
  20. my @data;
  21. my $file;
  22. my @fields;
  23.  
  24. foreach (@files) {
  25.    $file = IO::File->new($_, "r" );
  26.    unless (defined $file) { die "Ouverture de $_ impossible: $!"; }
  27.    while (<$file> ) {
  28.        chomp;
  29.        if (/^(:?[^;]*;){12}$/) {  # on ne prend que les lignes bien formées: 12 champs vides ou non, suivis de ;  
  30.                                            # -- a adapter s'il y a des blancs après le dernier ;
  31.            s/"//g;  #" commentaire inutile, ne sert que pour un bug de coloration du code sur le forum
  32.            @fields = split /;/, $_;
  33.            push @fields, substr($fields[3], 0, 3);  
  34.            $fields[12] =~ s/INA|INJ|INR/INT/;
  35.            push @data, "\"".$fields[0]."\",\"".$fields[2]."\",\"".$fields[3]."\",\"".$fields[4]."\",\""
  36.                              .$fields[5]."\",\"".$fields[6]."\",\"".$fields[11]."\",\"".$fields[12]."\"";
  37.        }
  38.    }
  39.    undef($file); #ferme automatiquement le fichier
  40. }
  41.  
  42.  
  43. my %seen = ();
  44. @data = sort grep { ! $seen{ $_ }++ } @data;  #vire les lignes dupliquées et trie alphabétiquement
  45. foreach (@data) {
  46.     print "insert into vod_billing values(".$_." );\n";
  47. }
 

Bon, la ligne @data = sort grep { ! $seen{ $_ }++ } @data;  fait un tri lexicographique sur les lignes (uniques)
S'il vous faut impérativement un tri numérique sur les valeurs numériques du premier champ, il faudra utiliser une fonction de tri adaptée.

 

A+,


Message édité par gilou le 11-11-2009 à 17:18:59

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

Marsh Posté le 11-11-2009 à 18:08:46    

Bonjour,
 
C'est vraiment du haut niveau  :pt1cable: chapeau  :jap:  
 
Cependant j'ai executé le script qui ne sort aucune erreur, mais il n'imprime rien à l'écran.
J'ai adapté mes exemples au votre en créant même un dossier C:\Perl mais cela ne change pas.
 
J'ai ajouté un print avant la l.43 et il s'affiche, mais pendant le foreach à la l.46 rien ne sort
 
Est-ce que les modules utilisés ne sont sans doute pas installé dans mon cygwin?
 
[edit]: j'ai effectué un cpan des 2 modules puis ré exécuté le script mais cela ne m'imprime toujours rien à l'écran.
Pourtant le script ne sort aucune erreur.


Message édité par Sethenssen le 11-11-2009 à 18:11:48
Reply

Marsh Posté le 11-11-2009 à 18:44:04    

Citation :

J'ai adapté mes exemples au votre en créant même un dossier C:\Perl mais cela ne change pas.

Euh, le répertoire la, c'est celui contenant vos données.
Donc a adapter a votre cas.
 
si vous faites un foreach (@files) {print $_,"\n";} a la ligne 19, ca vous imprime la liste de vos fichiers de donnée? Je soupçonne que la liste est vide au vu de ce que vous avez comme comportement.  
 

Citation :

Est-ce que les modules utilisés ne sont sans doute pas installé dans mon cygwin?

 
J'utilise le perl 5.10  d'active state, et les modules que j'utilise ici font partie du core, donc doivent être avec toute version.
 
A+,


Message édité par gilou le 11-11-2009 à 18:52:51

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

Marsh Posté le 11-11-2009 à 18:54:01    

Exact cela affiche bien mes données sur l'écran.
Cependant rien d'autre n'est affiché et le script ne retourne aucune erreur.
 
J'utilise également Perl 5.10 via cygwin


Message édité par Sethenssen le 11-11-2009 à 18:54:55
Reply

Marsh Posté le 11-11-2009 à 19:12:40    

Citation :

Exact cela affiche bien mes données sur l'écran.

Le print en ligne 19 affiche les noms des fichiers de donnée, en full path?
 
Si oui, il va falloir tracer, chez moi, je n'ai aucun pb.
 
Si vous faites un print $_, "\n"; avant la ligne  
s/"//g;  #" commentaire inutile, ne sert que pour un bug de coloration du code sur le forum
 
Ca vous imprime le contenu de vos fichiers de données ou non?
Si non, c'est parce que vos lignes ne sont pas au format /^(:?[^;]*;){12}$/  Il y a peut être des espaces qui posent pb. Essayez alors de remplacer /^(:?[^;]*;){12}$/  par /^(:?[^;]*;){12}\s*$/  
Si oui, c'est que le pb est ailleurs, et il faut continuer à tracer.
A+,


Message édité par gilou le 11-11-2009 à 19:13:40

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

Marsh Posté le 11-11-2009 à 19:12:40   

Reply

Marsh Posté le 11-11-2009 à 19:31:48    

Cela affiche les noms des fichiers de donnée en full path.
 
Si je mets un print $_, "\n";  avant la ligne 31 cela ne m'affiche rien.
 
Sans doute un problème avec /^(:?[^;]*;){12}$/  
Mais j'avoue que je n'arrive pas à dechiffrer son fonctionnement.
 
J'ai alors regardé mes données et il est possible d'avoir des espaces à la colonne [4]
C'est à dire qu'à la place de "Stylo" on peut avoir "Stylo Bleu" et du coup un espace.
 
J'ai alors tenté de modifier mes données en supprimant tout espace dans cette colonne mais cela n'affiche toujours rien.
 
Donc je ne sais pas où tracer sans savoir ce qu'est censer donner l'expression régulière malheureusement


Message édité par Sethenssen le 11-11-2009 à 19:32:11
Reply

Marsh Posté le 11-11-2009 à 19:42:35    

C'est tout bête: /^(:?[^;]*;){12}$/  
On matche une ligne qui contient des caractères différents de ; puis un ; et ceci 12 fois de suite:
[^;] => un caractère distinct de ;
[^;]* => zéro ou des caractères distincts de ;
[^;]*; => zéro ou des caractères distincts de ; suivis d'un ;
([^;]*;){12} => zéro ou des caractères distincts de ; suivis d'un ; 12 fois de suite
(:?[^;]*;){12} pareil, juste une optimisation mémoire
/^(:?[^;]*;){12}$/ Rien avant et rien après sur la ligne (^: début de ligne, $: fin de ligne)
Je me suis basé sur les données que vous avez donné en exemple, ou vos csv sont ainsi constitués
Si vous testez sur des données structurées différement, ca ne sera pas matché, et c'est ce qui doit poser problème ici.
 
Les espaces n'ont aucune importance dans les champs, mais s'il y en a avant la fin de ligne il faut en tenir compte en rajoutant un \s* avant le $
A+,


Message édité par gilou le 11-11-2009 à 19:45:11

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

Marsh Posté le 11-11-2009 à 20:11:18    

Merci beaucoup pour votre aide Gilou
Je vais travailler sur ça

Reply

Sujets relatifs:

Leave a Replay

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