Compter le nombre de doublons d'un tableau

Compter le nombre de doublons d'un tableau - Perl - Programmation

Marsh Posté le 28-10-2011 à 15:21:57    

Bonjour,
 
j'aimerais créer un script permettant de compter le nombre de doublons d'un tableau pour chaque cas. Par exemple, j'ai un CSV du type suivant (que je mets dans un Array) :
 

Citation :


14/04/2011,titi,serveur1
14/04/2011,titi,serveur2
14/04/2011,titi,serveur1
14/04/2011,titi,serveur1
15/04/2011,titi,serveur2
15/04/2011,titi,serveur1
15/04/2011,titi,serveur2
15/04/2011,titi,serveur1
15/04/2011,titi,serveur2
15/04/2011,titi,serveur2
18/04/2011,titi,serveur2
18/04/2011,titi,serveur1
18/04/2011,titi,serveur1
18/04/2011,titi,serveur1

J'ai déjà cette méthode qui permet de supprimer les doublons :
 

Code :
  1. sub doublons_grep
  2. {
  3.   my ($ref_tableau) = @_;
  4.   my %hash_sans_doublon;
  5.   return grep { !$hash_sans_doublon{$_}++ } @{$ref_tableau};
  6. }
  7. my @connections_sans_doublon = doublons_grep( \@connections );

Mon but final est d'obtenir un tableau qui récapitule le nombre de fois qu'un utilisateur s'est connecté au serveur 1 ou au serveur 2, par jour.

Bien sûr, dans mon fichier, je n'ai pas seulement l'utilisateur "titi" (j'ai en revanche uniquement 2 serveurs). Pourriez-vous me donner des conseils pour atteindre mon but ? Pas du code tout mâché, mais un algo ou la méthode pour procéder. Peut-être que la méthode "compter le nombre de doublons pour chaque cas, puis supprimer les doublons" que j'ai choisi n'est pas la meilleure...
 
Je précise que je ne suis pas très expérimenté en Perl, donc n'hésitez pas à détailler votre pensée. Merci d'avance pour vos réponses !


---------------
www.benji1000.net
Reply

Marsh Posté le 28-10-2011 à 15:21:57   

Reply

Marsh Posté le 28-10-2011 à 19:02:29    

Si j'ai bien compris, on veut amalgamer les cas du serveur 1 et du serveur 2 pour un même user, le même jour.
Le plus simple:
Tu définis un hash, pour lequel la clé va être l'info discriminante, et la valeur pour une clé, le nombre d'occurences dans le tableau.
 

Code :
  1. #!/usr/bin/perl
  2.  
  3. # error-check flags
  4. use strict;
  5. use warnings;
  6. # Use readable built-in names
  7. use English qw( -no_match_vars );
  8.  
  9. # array contenant les lignes inutile de te préoccuper de ce code,
  10. # c'est juste que c'était plus rapide a écrire que de passer par l'ouverture d'un fichier de données
  11. my @csv= <<EOT =~ m/^\s*(.*?)\s*\n/mg; # Le texte qui suit est decoupé en array par ligne, on en profite pour bouffer les espaces aux extremités de ligne
  12. 14/04/2011,titi,serveur1
  13. 14/04/2011,titi,serveur2
  14. 14/04/2011,titi,serveur1
  15. 14/04/2011,titi,serveur1
  16. 15/04/2011,titi,serveur2
  17. 15/04/2011,titi,serveur1
  18. 15/04/2011,titi,serveur2
  19. 15/04/2011,titi,serveur1
  20. 15/04/2011,titi,serveur2
  21. 15/04/2011,titi,serveur2
  22. 18/04/2011,titi,serveur2
  23. 18/04/2011,titi,serveur1
  24. 18/04/2011,titi,serveur1
  25. 18/04/2011,titi,serveur1
  26. EOT
  27.  
  28. my %myhash;
  29. my @parsed;
  30. foreach (@csv) {
  31.  @parsed = split /\s*\,\s*/, $_; # découpage en champs séparés par une virgule, on en profite pour bouffer les espaces autour des champs
  32.  $myhash{$parsed[0].".".$parsed[1]}++;  # clé formée sur la date et le nom
  33. }
  34.  
  35. print "rank  user  date\n";
  36. foreach (sort {$myhash{$b} <=> $myhash{$a}} (keys(%myhash))) { # On crée l'array des clés du hash, on l'ordonne par tri sur la valeur associée à la clé
  37.  m/([^.]+)\.(.*)/;  # regexp de retrouver les deux constituants de la clé dans $1 et $2
  38.  print $myhash{$_}, "  ", $2, "  ", $1, "\n";
  39. }


C:Perl>perl benji.pl
rank  user  date
6  titi  15/04/2011
4  titi  14/04/2011
4  titi  18/04/2011


 
Donc le principe,  
1) on découpe l'array en champs, avec split (ou une expresssion régulière ad-hoc, selon le format de ses données)
2) On fabrique une clé primaire par la concaténation des champs discriminants, ici, le premier champ $parsed[0] la date, et le second $parsed[1] le nom
3 on crée un hash dont la clé est cette clé primaire, et dont la valeur est incrémenté à chaque fois qu'elle est trouvée dans les données $myhash{$parsed[0].".".$parsed[1]}++;  
 
On aurait tout aussi bien pu faire:

Code :
  1. my %myhash;
  2. foreach (@csv) {
  3.  if (m#\s*(\d\d\/\d\d\/\d\d\d\d)\s*,\s*(\w+)\s*,\s*(\w+)\s*#) {
  4.      $myhash{$1.".".$2}++;
  5.    }
  6. }


 
 
Le reste, c'est l'application de la méthode standard
foreach (sort {$myhash{$b} <=> $myhash{$a}} (keys(%myhash))) {  
  print $myhash{$_}, "  ", $_, "\n";
}
qui permet d'imprimer un hash trié par valeur (le tri est numérique ici, mais il pourrait être lexicographique ou autre)
A+,


Message édité par gilou le 29-10-2011 à 16:33:16

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

Marsh Posté le 29-10-2011 à 15:32:27    

Bon, perso, si j'avais eu ceci à faire, j'aurais procédé ainsi:
 

Code :
  1. #!/usr/bin/perl
  2.  
  3. # error-check flags
  4. use strict;
  5. use warnings;
  6. # Use readable built-in names
  7. use English qw( -no_match_vars );
  8.  
  9. my %myhash;
  10. while (<> ) { # les lignes sont dans un fichier passé en paramètre au script
  11.  # clean up
  12.  chop;
  13.  s/^\s*//;
  14.  s/\s*$//;
  15.  s/\s*,\s*/,/g;
  16.  next unless /./;
  17.  # On a ici des lignes non vides nettoyées des blancs inutiles
  18.  my $key = (split(/([^,]+,[^,]+)/, $_))[1];
  19.  $myhash{$key} = [0, (split(/,/, $_))[1,0], join("", (split(/[\/,]/, $_))[2,1,0])] unless defined($myhash{$key});
  20.  $myhash{$key}[0]++;
  21. }
  22.  
  23. print "rank  user  date\n";
  24. foreach (sort {$myhash{$b}[0] <=> $myhash{$a}[0] or
  25.            $myhash{$a}[1] cmp $myhash{$b}[1] or
  26.            $myhash{$b}[3] <=> $myhash{$a}[3]} (keys(%myhash))) {
  27.  print "   ", join("  ", @{$myhash{$_}}[0..2]), "\n";
  28. }


 

C:\Perl>perl benji.pl benji.txt
rank  user  date
   6  titi  15/04/2011
   4  titi  18/04/2011
   4  titi  14/04/2011


 
c'est la même méthode, mais en plus sophistiqué:
 
Je créée toujours une clé discriminante pour un hash
my $key = (split(/([^,]+,[^,]+)/, $_))[1];  
je découpe par morceau contenant une virgule et encadré par deux virgules ou le début/fin de ligne, et je ne prends que le premier morceau,  
ça me donne pour une ligne "14/04/2011,titi,serveur1" la portion du début "14/04/2011,titi" qui est ce sur quoi on discrimine les lignes (date et nom)
 
Mais au lieu de donner au hash comme valeur le nb de fois que la clé à été rencontrée, je donne comme valeur au hash un tableau (anonyme)
$myhash{$key} = [ .. ]  
comme premier élément du tableau, je vais mettre le nb de fois que la clé à été rencontrée
 
ensuite je met le champ nom et le champ date, obtenus a coups de split sur la bonne regexp
(split(/,/, $_))[1,0]: on splitte la ligne sur la virgule, et on ne prend que les champs 1 et 0 donc ce qui est après la 1ère virgule et ce qui est avant, ie le nom et la date
 
enfin, pour pouvoir éventuellement trier sur la date, j'ajoute un élément composé de l'année suivi du mois suivi du jour, à coup de regexp:
join("", (split(/[\/,]/, $_))[2,1,0])
 
donc au final, pour une ligne comme
"14/04/2011,titi,serveur1"
j'ai une clé "14/04/2011, titi", et si le hash ne contient pas encore cette clé, je lui ajoute cette clé avec comme valeur le tableau
[1, titi, 14/04/2011, 20110414]
et si le hash contient déjà la clé, j'incrémente juste la première entrée du tableau
[En fait, dans le premier élément, je met 0 a la création, et j'incrémente systématiquement ensuite, c'est équivalent plus simple à relire.]
 
Ensuite, c'est le code d'impression comme précédemment mais un peu amélioré:
On trie par rang (tri numérique), à rang égal, on trie sur le nom (tri lexicographique) et a rang et nom égal, on trie sur la date (tri numérique sur le dernier champ, qui a été construit pour cela)
 
Ensuite on imprime le clé du hash et le tableau de ses valeurs sauf la dernière, qui n'était la que pour servir au tri.
 
L'intérêt ici c'est qu'en changeant l'ordre des tris, je peux exploiter autrement mes données.
 
Bon après, on peut utiliser les routines de format de perl, pour un rapport plus lisible:

Code :
  1. foreach (sort {$myhash{$b}[0] <=> $myhash{$a}[0] or
  2.            $myhash{$a}[1] cmp $myhash{$b}[1] or
  3.            $myhash{$b}[3] <=> $myhash{$a}[3]} (keys(%myhash))) {
  4.  write;
  5. }
  6. format STDOUT_TOP=
  7.  Rank    User Name          Date
  8. =====================================
  9. .
  10.  
  11. format STDOUT=
  12. @>>>>>  | @<<<<<<<<<<< | @<<<<<<<<< |
  13. $myhash{$_}[0], $myhash{$_}[1], $myhash{$_}[2]
  14. .


C:Perl>perl benji.pl benji.txt
  Rank    User Name          Date
=====================================
     6  | titi         | 15/04/2011 |
     4  | titi         | 18/04/2011 |
     4  | titi         | 14/04/2011 |


A+,


Message édité par gilou le 29-10-2011 à 16:18:01

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

Marsh Posté le 03-11-2011 à 14:06:46    

Merci pour ta réponse, gilou. Excuse-moi de ne pas t'avoir répondu, je n'ai pas été prévenu des nouveaux messages dans le topic (je croyais que la notification par mail était activée par défaut) :\
 
Bref, je venais pour clore le sujet. Celui-ci a été résolu sur le Site du Zéro, je te donne le lien, je pense que la méthode peut t'intéresser. Je la trouve plus simple que la tienne :
 
http://www.siteduzero.com/forum-83 [...] bleau.html


Message édité par benji1000 le 03-11-2011 à 14:08:30

---------------
www.benji1000.net
Reply

Marsh Posté le 03-11-2011 à 16:20:30    

Merci.
C'est plus ou moins ce que je faisais, sauf que tu sembles vouloir séparer les sites (j'avais cru que tu voulais les confondre), et qu'il y a une technique que je ne connaissais pas, car je ne l'avais jamais employée, et qui est super simple en effet [j'ai du relire trois fois le ++$h{$t[0]}{$t[1]}{$t[2]} pour que le déclic se fasse, et que je pense "Bon sang mais c'est bien sur..." ;)], la création de multiples hashs imbriqués. C'est du même style que ce que je faisais à la main, mais plus direct (en terme de lignes de code, avec ma création d'un tableau anonyme).
Je retiendrais dorénavant cette technique, car c'est assez pratique, ça simplifie des trucs que j'aurais fait à la main sinon.
 
A+,


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

Marsh Posté le 03-11-2011 à 18:59:46    

En reprenant mon code, et en utilisant cette astuce:  

Code :
  1. #!/usr/bin/perl
  2.  
  3. # error-check flags
  4. use strict;
  5. use warnings;
  6. # Use readable built-in names
  7. use English qw( -no_match_vars );
  8.  
  9. my (%myhash, %hsites);
  10. my ($date, $user, $site);
  11. while (<> ) {
  12.  # clean up
  13.  chop;
  14.  s/^\s*//;
  15.  s/\s*$//;
  16.  s/\s*,\s*/,/g;
  17.  next unless /./;
  18.  # On a ici des lignes non vides nettoyées des blancs inutiles
  19.  ($date, $user, $site) = split /,/;
  20.  ++$myhash{$date}{$user}{$site};
  21.  ++$hsites{$site};
  22. }
  23.  
  24. my @sites = sort (keys %hsites); # pour avoir tous les sites au moment du listage
  25.  
  26. #print the header
  27. print "   Date   \tUser\t";
  28. foreach (@sites) {
  29.  print $_, "\t";
  30. }
  31. print "\n";
  32.  
  33. foreach my $date (sort {join("", reverse(split(/\//, $b))) cmp join("", reverse(split(/\//, $a)))} (keys %myhash)) {
  34.  foreach my $user (sort (keys %{$myhash{$date}})) {
  35.    print $date, "\t", $user, "\t";
  36.    foreach my $site (@sites) {
  37.      if (defined($myhash{$date}{$user}{$site})) {
  38.     print $myhash{$date}{$user}{$site}, "\t\t";
  39.      }
  40.      else {
  41.     print "-", "\t\t";
  42.      }
  43.    }
  44.    print "\n";
  45.  }
  46. }


C:\Perl>perl benji.pl benji.txt
   Date         User    serveur1        serveur2        serveur3
01/05/2011      titi    -               -               1
18/04/2011      titi    3               1               -
18/04/2011      tutu    1               1               1
15/04/2011      titi    2               5               2
15/04/2011      toto    -               1               -
15/04/2011      tutu    2               4               -
14/04/2011      titi    3               1               1
14/04/2011      toto    3               1               -


 
Ou benji.txt est

14/04/2011,titi,serveur1  
14/04/2011,titi,serveur2  
14/04/2011,titi,serveur1  
14/04/2011,titi,serveur1  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur1  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur1  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur2  
18/04/2011,titi,serveur2  
18/04/2011,titi,serveur1  
18/04/2011,titi,serveur1  
18/04/2011,titi,serveur1
14/04/2011,toto,serveur1  
14/04/2011,toto,serveur2  
14/04/2011,toto,serveur1  
14/04/2011,toto,serveur1  
15/04/2011,toto,serveur2  
15/04/2011,tutu,serveur2  
15/04/2011,tutu,serveur1  
15/04/2011,tutu,serveur2  
15/04/2011,tutu,serveur1  
15/04/2011,tutu,serveur2  
15/04/2011,tutu,serveur2  
18/04/2011,tutu,serveur2  
18/04/2011,tutu,serveur1  
18/04/2011,tutu,serveur3  
14/04/2011,titi,serveur3  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur3  
15/04/2011,titi,serveur3  
01/05/2011,titi,serveur3

 
 
A+,


Message édité par gilou le 03-11-2011 à 19:07:15

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

Marsh Posté le 04-11-2011 à 09:33:48    

C'est vrai que là, le code est plus court ! J'aime particulièrement ta ligne 33, où tu tries les dates en une seule ligne, alors que moi c'était toute une étape de trier les dates !  
 
J'ai néanmoins plus simple à te suggérer pour remplacer les lignes 37 à 42 où tu testes l'existence de $myhash{$date}{$user}{$site}. Ne fais pas ce test, et print cette variable. Seulement, mets-là entre parenthèse et ajoute //0 à la fin. De cette façon, si elle n'existe pas, c'est la avleur 0 qui sera mise (enfin, pour toi, ce sera un tiret). Comme ceci :
 

Code :
  1. foreach my $site (@sites)
  2. {
  3. ($myhash{$date}{$user}{$site}//0)
  4. }


Vu que tu as l'air intéressé, j'aimerais bien te remettre à contribution ! J'aimerais à présent faire le total des connections par mois et par serveur. Pour obtenir quelque chose comme ceci :

Citation :

MOIS   SERVEUR 1  SERVEUR 2
Janvier  26    57
Février  13    14
Mars  50    39
...


À la base, j'ai réussi à créer un fichier CSV de la forme :

Citation :

Avril,utilisateura,SERVEUR1
Avril,utilisateurb,SERVEUR2
Avril,utilisateura,SERVEUR1
Avril,utilisateurc,SERVEUR1
Mai,utilisateura,SERVEUR2
Avril,utilisateurc,SERVEUR1


Aurais-tu une petite idée pour procéder ? je pense qu'il faudrait réutiliser le principe de l'auto-vivification, mais je n'ai pas réussi à l'appliquer pour ce cas :(


Message édité par benji1000 le 04-11-2011 à 09:35:23

---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 10:36:51    

Citation :

J'ai néanmoins plus simple à te suggérer pour remplacer les lignes 37 à 42 où tu testes l'existence de $myhash{$date}{$user}{$site}. Ne fais pas ce test, et print cette variable. Seulement, mets-là entre parenthèse et ajoute //0 à la fin.

J'aimerais bien savoir ou c'est documenté dans Perl, ça.
Parce que si tu veux pas un chiffre, est ce que ça marche encore avec une chaine, je vais tester :)
 
Pour le truc récapitulatif, inutile de changer de fichier je pense.  
Je vais y jeter un œil.
 
EDIT: Oui, ca marche aussi avec du texte. On peut remplacer la fin de mon code précédent par  

Code :
  1. foreach my $date (sort {join("", reverse(split(/\//, $b))) cmp join("", reverse(split(/\//, $a)))} (keys %myhash)) {
  2.  foreach my $user (sort (keys %{$myhash{$date}})) {
  3.    print $date, "\t", $user, "\t";
  4.    foreach my $site (@sites) {
  5.      print "", ($myhash{$date}{$user}{$site} // '-'), "\t\t";
  6.    }
  7.    print "\n";
  8.  }
  9. }


 
A+,


Message édité par gilou le 04-11-2011 à 10:45:11

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

Marsh Posté le 04-11-2011 à 10:44:07    

Je ne sais pas où c'est documenté :\ on m'a juste donné cette astuce sur un forum.
 
Si tu veux un chiffre en caractère par défaut, tu as juste à écrire ceci :

Code :
  1. print ($myhash{$date}{$user}{$site}//0);


Par contre, si tu veux une chaîne de caractère (ou un caractère), il faut préalablement faire un test :

Code :
  1. if (($myhash{$date}{$user}{$site}//0) == 0){$myhash{$date}{$user}{$site} = 'ta_chaine_de_char_exemple_tiret'}
  2. print $myhash{$date}{$user}{$site};


Merci de chercher ;)
 
EDIT : je viens de voir ton EDIT ^^ pour moi ça n'a pas marché de faire directement comme ça. Mais je concaténais la valeur avec une chaîne de caractère, apparemment y'a souvent des soucis avec la concaténation si on ne le fait pas comme il faut.

Message cité 1 fois
Message édité par benji1000 le 04-11-2011 à 10:46:55

---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 10:48:21    

benji1000 a écrit :

Je ne sais pas où c'est documenté :\ on m'a juste donné cette astuce sur un forum.
 
Si tu veux un chiffre en caractère par défaut, tu as juste à écrire ceci :

Code :
  1. print ($myhash{$date}{$user}{$site}//0);


Par contre, si tu veux une chaîne de caractère (ou un caractère), il faut préalablement faire un test :

Code :
  1. if (($myhash{$date}{$user}{$site}//0) == 0){$myhash{$date}{$user}{$site} = 'ta_chaine_de_char_exemple_tiret'}
  2. print $myhash{$date}{$user}{$site};


Merci de chercher ;)

Pas la peine: avec

Code :
  1. foreach my $date (sort {join("", reverse(split(/\//, $b))) cmp join("", reverse(split(/\//, $a)))} (keys %myhash)) {
  2.  foreach my $user (sort (keys %{$myhash{$date}})) {
  3.    print $date, "\t", $user, "\t";
  4.    foreach my $site (@sites) {
  5.      print "", ($myhash{$date}{$user}{$site} // "****" ), "\t\t";
  6.    }
  7.    print "\n";
  8.  }
  9. }


ça me donne
 

  Date         User    serveur1        serveur2        serveur3
01/05/2011      titi    ****            ****            1
18/04/2011      titi    3               1               ****
18/04/2011      tutu    1               1               1
15/04/2011      titi    2               5               2
15/04/2011      toto    ****            1               ****
15/04/2011      tutu    2               4               ****
14/04/2011      titi    3               1               1
14/04/2011      toto    3               1               ****


 
Bon, je regarde le reste.
 
A+,


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

Marsh Posté le 04-11-2011 à 10:48:21   

Reply

Marsh Posté le 04-11-2011 à 11:16:33    

Il suffisait d'ajouter un nouveau hash, et de faire
++$newhash{substr($date,3,2)}{$site};
On prend comme clé le mois et ensuite le site.
Note: ça suppose néanmoins que on ne fait ça que sur 12 mois, car ça ne se préoccupe pas de l'année (sinon, il faudrait faire quelque chose comme ++$newhash{substr($date,3,2)}{substr($date,-4)}{$site}; et tenir compte de l'année dans le code d'affichage)

Code :
  1. #!/usr/bin/perl
  2.  
  3. # error-check flags
  4. use strict;
  5. use warnings;
  6. # Use readable built-in names
  7. use English qw( -no_match_vars );
  8.  
  9. my (%myhash, %hsites);
  10. my ($date, $user, $site);
  11. my (%newhash);
  12. while (<> ) {
  13.  # clean up
  14.  chop;
  15.  s/^\s*//;
  16.  s/\s*$//;
  17.  s/\s*,\s*/,/g;
  18.  next unless /./;
  19.  # On a ici des lignes non vides nettoyées des blancs inutiles
  20.  ($date, $user, $site) = split /,/;
  21.  ++$myhash{$date}{$user}{$site};
  22.  ++$hsites{$site};
  23.  ++$newhash{substr($date,3,2)}{$site};
  24. }
  25.  
  26. my @sites = sort (keys %hsites); # pour avoir tous les sites au moment du listage
  27.  
  28. #print the header
  29. print "   Date   \tUser\t";
  30. foreach (@sites) {
  31.  print $_, "\t";
  32. }
  33. print "\n";
  34.  
  35. foreach my $date (sort {join("", reverse(split(/\//, $b))) cmp join("", reverse(split(/\//, $a)))} (keys %myhash)) {
  36.  foreach my $user (sort (keys %{$myhash{$date}})) {
  37.    print $date, "\t", $user, "\t";
  38.    foreach my $site (@sites) {
  39.      print "", ($myhash{$date}{$user}{$site} // "****" ), "\t\t";
  40.    }
  41.    print "\n";
  42.  }
  43. }
  44.  
  45. printf "\n\n";
  46. #print the header
  47. print "Mois   \t";
  48. foreach (@sites) {
  49.  print $_, "\t";
  50. }
  51. print "\n";
  52.  
  53. my @months = qw{Decembre Janvier Fevrier Mars Avril Mai Juin Juillet Aout Septembre Octobre Novembre Decembre};
  54.  
  55. foreach my $month (sort (keys %newhash)) {
  56.  print $months[$month], "\t";
  57.  foreach my $site (@sites) {
  58.    print "", ($newhash{$month}{$site} // 0), "\t\t";
  59.  }
  60.  print "\n";
  61. }


C:\Perl>perl benji.pl benji.txt
   Date         User    serveur1        serveur2        serveur3
01/05/2011      titi    ****            ****            1
18/04/2011      titi    3               1               ****
18/04/2011      tutu    1               1               1
15/04/2011      titi    2               5               2
15/04/2011      toto    ****            1               ****
15/04/2011      tutu    2               4               ****
14/04/2011      titi    3               1               1
14/04/2011      toto    3               1               ****
 
 
Mois    serveur1        serveur2        serveur3
Avril   14              14              4
Mai     0               0               1


 
A+,


Message édité par gilou le 04-11-2011 à 11:21:31

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

Marsh Posté le 04-11-2011 à 11:20:13    

C'est bien ça, on ne se préoccupe pas de l'année. C'est juste pour voir quel serveur est le plus utilisé. J'essaie d'adapter ton code et je reviens te voir si j'ai un soucis. Merci bien ;)


---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 12:36:44    

J'ai essayé d'adapter ton script au mien, mais ça ne fonctionne pas :\ aucun message d'erreur, mais ça n’affiche pas le tableau...
 

Code :
  1. open($temp3,"temp3.csv" ) || die ("Erreur de lecture de temp3.csv" );
  2. my (%myhash, %hsites);
  3. my ($date, $user, $site);
  4. my (%newhash);
  5. while (my $m = <$temp3> )
  6. {
  7.     chomp $m;
  8.     ($date, $user, $site) = split ',', $m;
  9.     ++$myhash{$date}{$user}{$site};
  10.     ++$hsites{$site};
  11.     ++$newhash{substr($date,3,2)}{$site};
  12. }
  13. my @sites = sort (keys %hsites); # pour avoir tous les sites au moment du listage
  14. my @months = qw{Avril Mai Juin Juillet Aout Septembre};
  15. foreach my $month (sort (keys %newhash))
  16. {
  17.     print $html '<tr><td>'.$newhash{$month}.'</td><td>';
  18.     foreach my $site (@sites)
  19.     {
  20.         print $html '<td>'.($newhash{$month}{$site}//0).'</td>';
  21.     }
  22.     print $html '</tr>';
  23. }
  24. close($temp3);


Les données sont stockées dans temp3.csv et j'ai des mois allant seulement de Avril à Septembre (mais après je pourrai rajouter d'autres mois, c'est pas ça le problème).
Concernant le HTML, <tr> c'est pour déclarer uen ligne et </tr> c'est pour la fermer. Pour les cases, c'est <td> et </td>.


Message édité par benji1000 le 04-11-2011 à 12:38:24

---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 12:47:24    

Bon, je dois aller bouffer, je regarderais après.
Attention néanmoins:
Il faut impérativement:
my @months = qw{Decembre Janvier Fevrier Mars Avril Mai Juin Juillet Aout Septembre Octobre Novembre Decembre};
pour faire la correspondance valeur de $mois -> nom de mois (et le fait de démarrer à Décembre est pour que 1 corresponde a janvier).
 
La tu fais:
my @months = qw{Avril Mai Juin Juillet Aout Septembre};
ça peut pas marcher correctement car tu fais correspondre le mois 1 à Mai.
 
A+,


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

Marsh Posté le 04-11-2011 à 13:26:58    

Bon, j'ai vu ta seconde erreur:
remplaces
print $html '<tr><td>'.$newhash{$month}.'</td><td>';
par
print $html '<tr><td>'.$months[$month].'</td><td>';
comme dans mon code
 
A+,


Message édité par gilou le 04-11-2011 à 13:31:08

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

Marsh Posté le 04-11-2011 à 13:30:10    

Pour l'opérateur //, j'ai trouvé l'info:
 

Citation :

It's the defined-or operator that was added in Perl 5.10. It's like ||
but tests for definedness instead of truth.


 
Très utile pour initialiser une variable si elle ne l'est pas encore:
$mylang //= "en":
par exemple.
 
A+,


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

Marsh Posté le 04-11-2011 à 13:56:20    

Merci de la précision :)
 
Sinon, pour le problème principal, ça ne fonctionne toujours pas, même en mettant tous les mois comme tu as fait :\ je ne vois pas d'où vient l'erreur, puisque j'ai quasiment repris ce que tu as fait, j'ai juste changé pour mettre au format HTML. Et je n'ai aucune erreur !


---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 14:17:00    

Mais chez moi, ça marche, hein...
En adaptant ton code pour sortir sur stdout, j'ai:
 

Code :
  1. #!/usr/bin/perl
  2.  
  3. # error-check flags
  4. use strict;
  5. use warnings;
  6. # Use readable built-in names
  7. use English qw( -no_match_vars );
  8. use Data::Dumper;
  9.  
  10. my (%myhash, %hsites);
  11. my ($date, $user, $site);
  12. my (%newhash);
  13. while (<> )
  14. {
  15.    chomp;
  16.    s/\s*$//o;
  17.    ($date, $user, $site) = split ',';
  18.    ++$myhash{$date}{$user}{$site};
  19.    ++$hsites{$site};
  20.    ++$newhash{substr($date,3,2)}{$site};
  21. }
  22.  
  23.  
  24. my @sites = sort (keys %hsites); # pour avoir tous les sites au moment du listage
  25.  
  26. my @months = qw{Decembre Janvier Fevrier Mars Avril Mai Juin Juillet Aout Septembre Octobre Novembre Decembre};
  27.  
  28. foreach my $month (sort (keys %newhash))
  29. {
  30.    print '<tr><td>'.$months[$month].'</td><td>';
  31.    foreach my $site (@sites)
  32.    {
  33.        print '<td>'.($newhash{$month}{$site}//0).'</td>';
  34.    }
  35.    print '</tr>', "\n";
  36. }


 

C:\Perl>perl benji.pl benji.txt
<tr><td>Avril</td><td><td>14</td><td>14</td><td>4</td></tr>
<tr><td>Mai</td><td><td>0</td><td>0</td><td>1</td></tr>


 
Notes le   s/\s*$//o; après le chomp, important, si tu ne veux pas avoir des sites différents a cause d'espaces qui trainent en fin de ligne.
 
Tu n'as pas changé le format du fichier de données?
Dans mes tests, benji.txt est

14/04/2011,titi,serveur1  
14/04/2011,titi,serveur2  
14/04/2011,titi,serveur1  
14/04/2011,titi,serveur1  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur1  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur1  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur2  
18/04/2011,titi,serveur2  
18/04/2011,titi,serveur1  
18/04/2011,titi,serveur1  
18/04/2011,titi,serveur1
14/04/2011,toto,serveur1  
14/04/2011,toto,serveur2  
14/04/2011,toto,serveur1  
14/04/2011,toto,serveur1  
15/04/2011,toto,serveur2  
15/04/2011,tutu,serveur2  
15/04/2011,tutu,serveur1  
15/04/2011,tutu,serveur2  
15/04/2011,tutu,serveur1  
15/04/2011,tutu,serveur2  
15/04/2011,tutu,serveur2  
18/04/2011,tutu,serveur2  
18/04/2011,tutu,serveur1  
18/04/2011,tutu,serveur3  
14/04/2011,titi,serveur3  
15/04/2011,titi,serveur2  
15/04/2011,titi,serveur3  
15/04/2011,titi,serveur3  
01/05/2011,titi,serveur3

 
 
A+,


Message édité par gilou le 04-11-2011 à 14:20:04

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

Marsh Posté le 04-11-2011 à 14:44:00    

Merci, voilà ce que j'ai comme erreurs :

Argument "" isn't numeric in array element at C:\chemin\script.pl line 203, <$temp3> line 836.
Argument "il" isn't numeric in array element at C:\chemin\script.pl line 203, <$temp3> line 836.
Argument "ll" isn't numeric in array element at C:\chemin\script.pl line 203, <$temp3> line 836.
Argument "n" isn't numeric in array element at C:\chemin\script.pl line 203, <$temp3> line 836.
Argument "t" isn't numeric in array element at C:\chemin\script.pl line 203, <$temp3> line 836.
Argument "te" isn't numeric in array element at C:\chemin\script.pl line 203, <$temp3> line 836.

La ligne 203 du script correspond à :

Code :
  1. print $recap '<tr><td>'.$months[$month].'</td>;

Du coup, en résultat, dans la colonne "Mois", j'ai uniquement "Decembre" pour toutes les lignes ! Mais la fonction de comptage marche bien :)


Message édité par benji1000 le 04-11-2011 à 14:45:10

---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 14:52:50    

clairement, la variable month récupérée comme clef de keys %newhash est incorrecte.
Il semble que  ($date, $user, $site) = split ','; ne t'envoie pas la date au format dd/mm/yyyy dans $date
Donc comme je le disais, tes données n'ont pas changé de format?
A+,


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

Marsh Posté le 04-11-2011 à 15:04:54    

Oui, suis-je bête ^^ j'avais déjà converti les dates avec les mois. Remarque, je te l'avais dit dans un de mes messages, tu n'as pas dû faire gaffe (j'avais déjà des données de la forme Avril,utilisateura,SERVEUR1). J'essaie de tout remettre comme il faut, ça devrait aller sans pb, et je te tiens au jus !
 
EDIT : au poil, merci beaucoup pour ton aide :jap:


Message édité par benji1000 le 04-11-2011 à 15:08:05

---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 15:07:00    

Tu serais pas parti de ton csv avec les noms de mois au début?
Parce que ca expliquerait le "il" comme substr($date,3,2) sur avril, le ll comme substr($date,3,2) sur juillet, etc.
Je pensais que tu avais vu que je changeais pas de fichier de log, puisque c'était inutile.
EDIT: nos posts se sont croisés.
C'est en général plus couteux (espace disque...) de générer un fichier de log que d'exploiter un même fichier de log avec des scripts perls adaptés au résultat recherché.
A+,


Message édité par gilou le 04-11-2011 à 15:08:48

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

Marsh Posté le 04-11-2011 à 15:09:02    

Yep, c'est bien ça :) j'ai pas un grand niveau, donc des opérations que tu effectues rapidement, je mets souvent plusieurs lignes de codes à les faire, pour pas grand chose au final ^^


---------------
www.benji1000.net
Reply

Marsh Posté le 04-11-2011 à 15:13:02    

C'est juste une question d'expérience.
Plus on fait de scripts perl, plus on s’aperçoit que c'est pratique à utiliser.
 
Bon, quand Perl6 sera un jour utilisable, ce sera encore plus puissant (mais il y a diverses habitudes d'écriture qui auront changé et auquel il faudra s'adapter).
 
A+,


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

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

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