IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Perl Discussion :

transformer un fichier CSV en Perl


Sujet :

Langage Perl

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Architecte de système d'information
    Inscrit en
    Janvier 2018
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Architecte de système d'information
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2018
    Messages : 20
    Par défaut transformer un fichier CSV en Perl
    Bonjour a tous ,

    J'ai un petit souci et je souhaiteavoir un peu d'aide SVP SVP

    Je dois faire un programme Perl qui prend en entrer un fichier CSV sous ce format :

    DateDebut;DateFin;Serveur;process
    2018-01-24 00:19;2018-01-24 00:30;SERVEUR1;PROCESS1
    2018-01-24 00:13;2018-01-24 00:31;SERVEUR2;PROCESS2
    2018-01-24 00:45;2018-01-24 00:55;SERVEUR1;PROCESS1

    C'est la date de début et de fin d'un processus.

    Je souhaite avoir a la fin ce format :

    date;serveur;process;status
    2018-01-24 00:00;SERVEUR1;PROCESS1;0
    2018-01-24 00:00;SERVEUR2;PROCESS2;0
    2018-01-24 00:05;SERVEUR1;PROCESS1;0
    2018-01-24 00:05;SERVEUR2;PROCESS2;0
    2018-01-24 00:10;SERVEUR1;PROCESS1;0
    2018-01-24 00:10;SERVEUR2;PROCESS2;0
    2018-01-24 00:15;SERVEUR1;PROCESS1;0
    2018-01-24 00:15;SERVEUR2;PROCESS2;1
    2018-01-24 00:20;SERVEUR1;PROCESS1;1
    2018-01-24 00:20;SERVEUR2;PROCESS2;1
    2018-01-24 00:25;SERVEUR1;PROCESS1;1
    2018-01-24 00:25;SERVEUR2;PROCESS2;1
    2018-01-24 00:30;SERVEUR1;PROCESS1;1
    2018-01-24 00:30;SERVEUR2;PROCESS2;1
    2018-01-24 00:35;SERVEUR1;PROCESS1;0
    2018-01-24 00:35;SERVEUR2;PROCESS2;0
    2018-01-24 00:40;SERVEUR1;PROCESS1;0
    2018-01-24 00:40;SERVEUR2;PROCESS2;0
    2018-01-24 00:45;SERVEUR1;PROCESS1;1
    2018-01-24 00:45;SERVEUR2;PROCESS2;0
    2018-01-24 00:50;SERVEUR1;PROCESS1;1
    2018-01-24 00:50;SERVEUR2;PROCESS2;0
    2018-01-24 00:55;SERVEUR1;PROCESS1;1
    2018-01-24 00:55;SERVEUR2;PROCESS2;0
    2018-01-24 01:00;SERVEUR1;PROCESS1;0
    2018-01-24 01:00;SERVEUR2;PROCESS2;0
    2018-01-24 01:05;SERVEUR1;PROCESS1;0
    2018-01-24 01:05;SERVEUR2;PROCESS2;0
    .
    .
    .
    2018-01-24 23:55;SERVEUR1;PROCESS1;0
    2018-01-24 23:55;SERVEUR2;PROCESS2;0

    il s'agit d'afficher chaque 5 minutes l’état du processus (Actif = 1 ou non actif = 0) de 00:00 jusqu'a 23:55..

    Pouvez vous m'aider SVP

    Merci beaucoup

  2. #2
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    la difficulté réside dans la gestion des dates et heures-minutes et des arrondis (toutes les 5 minutes). Il existe de nombreux modules de gestion de dates sur le CPAN, mais ceux auxquels j'ai pensé ne convenaient pas trop au besoin spécifique. Je préfère donc convertir en interne toutes les dates en time stamps (nombre de secondes écoulées depuis la date origine, le 01/01/1970), en arrondissant à la tranche de 5 minutes au-dessus (dates de début) ou en-dessous (dates de fin). Bien sûr, à la fin, il faut reconvertir les time stamps en dates au format désiré.

    J'ai donc écrit trois fonctions de conversion et d'arrondi suivantes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    use strict;
    use warnings;
    use Time::Local;
    use feature "say";
     
    sub timestamp2date {
        my $timestamp = shift;
        my ($sec, $min, $hour, $day, $month, $year) = localtime $timestamp;
        $year += 1900;
        $month += 1;
        ($min, $hour, $day, $month) = map { sprintf "%02d", $_ } $min, $hour, $day, $month; # préfixer avec un zéro quand il n'y a qu'un chiffre
        return "$year-$month-$day $hour:$min";
    }
     
    sub date2timestamp {
        # format: 2018-01-24 00:19
        my ($date_in, $round) = @_;
        my ($year, $month, $day, $hour, $min) = split /[- :]/, $date_in;
        $year -= 1900;
        $month -= 1;
        my $rounding_correction_sec = find_rounding($min, $round);
        my $timestamp = timelocal (0, $min, $hour, $day, $month, $year);
        return $timestamp + $rounding_correction_sec;
    }
     
    sub find_rounding {
        my ($min, $round) = @_;
        return 0 unless $round;
        my $rest_div = $min % (abs $round);
        return 0 unless $rest_div;
        if ($round < 0) {
            # On renvoie le nombre de secondes à retrancher
            return -$rest_div * 60;
        } else {
            # nombre de seconde à ajouter
            return ($round - $rest_div) * 60;
         }
    }
     
    # Tests de conversion: conversion en time stamp puis reconversion en date
    my $dat = "2018-01-24 00:19";
    say timestamp2date(date2timestamp($dat,  5));  # Arrondi 5 mn au-dessus  -> 2018-01-24 00:20
    say timestamp2date(date2timestamp($dat, -5));  # Arrondi 5 mn en-dessous -> 2018-01-24 00:15
    Écrire le reste du programme est assez simple. Au vu de tes données, j'ai pris les hypothèses simplificatrices suivantes:
    • Toutes les dates du fichier en entrée appartiennent au même jour ;
    • PROCESS1 a toujours lieu sur SERVEUR1 et PROCESS2 sur SERVEUR2;


    Il n'y aurait pas grand chose à changer si ces hypothèses sont erronées.

    J'utilise un hachage (en fait un hash de hash de hash) afin de stocker pour chaque time stamp (toutes les 5 minutes) s'il y a un process qui tourne due l'un ou l'autre serveur. Ici, il y avait deux stratégies possibles:
    • Ne stocker que les instants où un processus tourne et gérer les autres instants (non présents dans le hachage) à l'affichage à la fin;
    • Pré-remplir le hachage (avec des 0) pour tous les instants possibles entre 0h00 et 23h55, et passer la valeur à 1 pour les valeurs trouvées dans le fichier.

    Dans le code ci-dessous, j'ai choisi la seconde solution. Je ne remets pas ici le code des trois fonctions de calcul de dates ci-dessus mais il faudra bien sûr l'ajouter.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
     
    use strict;
    use warnings;
    use Time::Local;
    use feature "say";
    use constant { SECONDS_IN_A_DAY => 60 * 60 * 24, FIVE_MINUTES => 60 * 5 };
     
    # Ici, code des trois fonctions de conversion et d'arrondi donné plus haut 
     
    sub display {
        my $hash_ref = shift;
        for my $clef (sort keys %$hash_ref) {
            my $date = timestamp2date($clef);
            say $date, ";SERVEUR$_;PROCESS$_;", $hash_ref->{$clef}{"SERVEUR$_"}{"PROCESS$_"} for 1..2;
        }
    }
     
    my $first_line = <DATA>;
    my $day = $1 if $first_line =~ /^(\d{4}-\d\d-\d\d) /;
    my $start_of_day_ts = date2timestamp ("${day} 00:00", 0);
    my $end_of_day_ts = $start_of_day_ts + SECONDS_IN_A_DAY - FIVE_MINUTES;  # même jour à 23h55
    my %intervals;
    # Création de toutes les périodes entre 00h00 et 23h55
    for (my $i = $start_of_day_ts; $i <= $end_of_day_ts; $i += FIVE_MINUTES ) {
        $intervals{$i}{"SERVEUR$_"}{"PROCESS$_"} = 0 for 1..2;
    }
    process_line($first_line);
    # display(\%intervals);
     
     
    while (<DATA>) {
        process_line ($_);
    }
     
    sub process_line {
        my $line = shift;
        chomp $line;
        my ($debut, $fin, $serveur, $process) = split /;/, $line;
        my $start = date2timestamp($debut, 5); # début -> arrondi vers le haut
        my $end   = date2timestamp($fin,  -5); # fin -> arrondi vers le bas
        for (my $i = $start; $i <= $end; $i += FIVE_MINUTES) {
            $intervals{$i}{$serveur}{$process} = 1;
        }
    }
     
    display(\%intervals);
     
     
    __DATA__
    2018-01-24 00:19;2018-01-24 00:30;SERVEUR1;PROCESS1
    2018-01-24 00:13;2018-01-24 00:31;SERVEUR2;PROCESS2
    2018-01-24 00:45;2018-01-24 00:55;SERVEUR1;PROCESS1
    Le code affiche ce qui suit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
     
    $ perl intervalles_temps.pl
    2018-01-24 00:00;SERVEUR1;PROCESS1;0
    2018-01-24 00:00;SERVEUR2;PROCESS2;0
    2018-01-24 00:05;SERVEUR1;PROCESS1;0
    2018-01-24 00:05;SERVEUR2;PROCESS2;0
    2018-01-24 00:10;SERVEUR1;PROCESS1;0
    2018-01-24 00:10;SERVEUR2;PROCESS2;0
    2018-01-24 00:15;SERVEUR1;PROCESS1;0
    2018-01-24 00:15;SERVEUR2;PROCESS2;1
    2018-01-24 00:20;SERVEUR1;PROCESS1;1
    2018-01-24 00:20;SERVEUR2;PROCESS2;1
    2018-01-24 00:25;SERVEUR1;PROCESS1;1
    2018-01-24 00:25;SERVEUR2;PROCESS2;1
    2018-01-24 00:30;SERVEUR1;PROCESS1;1
    2018-01-24 00:30;SERVEUR2;PROCESS2;1
    2018-01-24 00:35;SERVEUR1;PROCESS1;0
    2018-01-24 00:35;SERVEUR2;PROCESS2;0
    2018-01-24 00:40;SERVEUR1;PROCESS1;0
    2018-01-24 00:40;SERVEUR2;PROCESS2;0
    2018-01-24 00:45;SERVEUR1;PROCESS1;1
    2018-01-24 00:45;SERVEUR2;PROCESS2;0
    2018-01-24 00:50;SERVEUR1;PROCESS1;1
    2018-01-24 00:50;SERVEUR2;PROCESS2;0
    2018-01-24 00:55;SERVEUR1;PROCESS1;1
    2018-01-24 00:55;SERVEUR2;PROCESS2;0
    2018-01-24 01:00;SERVEUR1;PROCESS1;0
    2018-01-24 01:00;SERVEUR2;PROCESS2;0
     ...
    2018-01-24 23:45;SERVEUR2;PROCESS2;0
    2018-01-24 23:50;SERVEUR1;PROCESS1;0
    2018-01-24 23:50;SERVEUR2;PROCESS2;0
    2018-01-24 23:55;SERVEUR1;PROCESS1;0
    2018-01-24 23:55;SERVEUR2;PROCESS2;0
    Ce qui paraît correspondre à ton besoin.

  3. #3
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 986
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 986
    Par défaut
    J'ai tenté une version qui ne cherche pas à arrondir (le processus tourne à cette heure ou ne tourne pas, point barre), mais par contre, qui autorise n'importe quel processus sur n'importe quel serveur.
    Le principe consiste à stocker les tranches horaires d'un processus dans des tableaux, eux-mêmes stockés dans un hash de hash (serveur, processus). Pour chaque date (variant de 5 minutes en 5 minutes) on vérifie si parmi les tableaux (pour un serveur et un processus donnés), il existe une tranche horaire contenant cette date. J'ai aussi fait l'hypothèse que le fichier de départ ne concerne qu'une seule et même journée.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    use strict;
    use warnings;
    use feature 'say';
     
    my $file = 'plages.csv';
     
    open(my $fh, '<', $file) or die "Could not open '$file' $!\n";
     
    my ($start, $end, $srv, $proc);
    my %h;
     
    for (<$fh>) {
        chomp;
        ($start, $end, $srv, $proc) = split ';';
        $h{$srv}{$proc} = [] unless exists $h{$srv}{$proc};
        push $h{$srv}{$proc}, [$start, $end];
    }
     
    close($fh);
     
    my $day = (split ' ', $start)[0];
    my $dt;
     
    for (my $m = 0; $m < 1440; $m += 5) {
        $dt = sprintf "%s %02d:%02d", $day, $m/60, $m%60;
        for $srv (sort keys %h) {
            for $proc (sort keys $h{$srv}) {
                say join ';', ($dt, $srv, $proc, is_running($dt, $h{$srv}{$proc}));
            }
        }    
    }
     
    sub is_running {
        my ($dt, $spns) = @_;
     
        for my $span (@$spns) {
            return 1 if ($dt ge $span->[0] and $dt le $span->[1]);
        }
        return 0;
    }
    J'ai également une autre version, qui fonctionne sur le même principe, mais faisant appel à des modules pour la gestion des csv et des tranches horaires:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    use strict;
    use warnings;
    use feature 'say';
    use Text::CSV;
    use DateTime::SpanSet;
    use DateTime::Format::Strptime;
     
    my $file = "plages.csv";
     
    open(my $fh, '<', $file) or die "Could not open '$file' $!\n";
     
    my ($start, $end, $srv, $proc, %proc_spns, $spn);
    my $csv = Text::CSV->new({ sep_char => ';' });
    my $strp = DateTime::Format::Strptime->new( pattern   => '%F %R',
                                                time_zone => 'UTC',
                                                on_error  => 'croak' );
     
    while (<$fh>) {
        if ( $csv->parse($_) ) {
            ($start, $end, $srv, $proc) = $csv->fields();
            $spn = DateTime::Span->from_datetimes( start => $strp->parse_datetime($start),
                                                   end   => $strp->parse_datetime($end) );
     
            $proc_spns{$srv}{$proc} = DateTime::SpanSet->empty_set
                unless exists $proc_spns{$srv}{$proc};
     
            $proc_spns{$srv}{$proc} = $proc_spns{$srv}{$proc}->union($spn);
        }
    }
    close($fh);
     
    my (@procs, @record);
    my @srvs = sort keys %proc_spns;
    my $duration = DateTime::Duration->new(minutes => 5);
    my ($day_start, $day_end) = ( $strp->parse_datetime($start =~ s/\d+:\d+/00:00/r), # je n'ai pas trouvé de solution autre pour faire ça
                                  $strp->parse_datetime($start =~ s/\d+:\d+/23:59/r) );
     
    for ( my $cur_time = $day_start; $cur_time < $day_end; $cur_time = $cur_time->add($duration) ) {
        for $srv (@srvs) {
            @procs = sort keys $proc_spns{$srv};
            say join ';', ($cur_time->strftime('%F %R'), $srv, $_, $proc_spns{$srv}{$_}->contains($cur_time)) for (@procs);
        }
    }

  4. #4
    Membre averti
    Homme Profil pro
    Architecte de système d'information
    Inscrit en
    Janvier 2018
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Architecte de système d'information
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2018
    Messages : 20
    Par défaut Merci beaucoup
    Merci beaucoup lolo78 et CosmoKnacki pour vos réponses très constructives c'est très gentille de votre part. J'ai maintenant hâte de tester..
    Malheureusement je suis malade donc cloué au lit pour le moment :/
    Mais dès mon rétablissement je vous tiens au courant des résultats.
    Je suis entrain de visualiser le code avec mon smartphone.
    Est il simple d'écrire les résultats dans un ficher csv et de manipuler la date ( par exemple prendre un fichier d'entrer avec une date j-1) ?
    Je suis expert en talend j'ai fait la même chose en l'espace de quelques minutes mais je débute en Perl :/

  5. #5
    Rédacteur/Modérateur

    Avatar de Lolo78
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Mai 2012
    Messages
    3 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2012
    Messages : 3 612
    Billets dans le blog
    1
    Par défaut
    Bonjour,
    oui, c'est très simple. Écrire dans un fichier au lieu d'afficher à l'écran demande simplement d'ouvrir préalablement le fichier en écriture, puis d'écrire dans le fichier au lieu d'écrire à l'écran.

    Ajouter ce qui suit au début du programme pour ouvrir le fichier de sortie:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    my $fic_out = "fic_sortie.csv";
    open my $OUT, ">",  $fic_out or die "ouverture impossible de $fic_out $!";
    Puis, au moment de l'écriture, modifier la ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    say $OUT $date, ";SERVEUR$_;PROCESS$_;", $hash_ref->{$clef}{"SERVEUR$_"}{"PROCESS$_"} for 1..2;
    Modifier la date n'est pas non plus compliqué, mais il faudrait que tu précises exactement ce que tu veux faire.

  6. #6
    Membre averti
    Homme Profil pro
    Architecte de système d'information
    Inscrit en
    Janvier 2018
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Architecte de système d'information
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Janvier 2018
    Messages : 20
    Par défaut Tests
    Merci beaucoup pour votre aide précieuse.

    J'ai testé les codes

    lolo78, votre code affiche tout le temps SERVEUR1ou2; PROCESS1ou2; même si je change les nom des serveurs et des process d'entré.

    CosmoKnacki, votre code répond exactement à mon besoin, je vais tester la version avec la DateTime mais je n'ai pas le package je vais voir comment le convertir avec Time.

    Pour l'ecriture en CSV c'est bon

    Il reste un petit souci au niveau des gestions des dates ( des fois un processus peut commencer à J-1 ou bien avant et se termine à J ou J+1 et plus)

    Exp :

    DateDebut;DateFin;Serveur;process
    2018-01-22 00:19;2018-01-24 00:30;SERVEUR1;PROCESS1
    2018-01-24 00:13;-- :;SERVEUR2;PROCESS2
    2018-01-21 00:45;-- :;SERVEUR1;PROCESS3

    "-- :" ==> process non terminé.

    le traitement doit passer le 2018-01-25 pour traiter les données de la veille.

    je souhaite que le code interprète les données de cette manière :

    DateDebut;DateFin;Serveur;process
    2018-01-24 00:00;2018-01-24 00:30;SERVEUR1;PROCESS1
    2018-01-24 00:13;2018-01-24 23:59;SERVEUR2;PROCESS2
    2018-01-24 00:00;2018-01-24 23:59;SERVEUR1;PROCESS3

    c'est-à-dire s les dates inférieures aux 24-01-2018 nous allons les traités comme s'ils étaient le 2018-01-24 00::00
    et toutes les dates non terminé seront traitées comme s'ils étaient 2018-01-24 23:59.



+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Transformation tableau -> fichier csv avec opération
    Par archibald78 dans le forum Excel
    Réponses: 0
    Dernier message: 03/06/2011, 18h02
  2. transformer un fichier csv en un autre avec awk
    Par nizartl dans le forum Shell et commandes GNU
    Réponses: 1
    Dernier message: 28/01/2011, 16h02
  3. Comment créer un fichier csv en Perl?
    Par mahmoudelect dans le forum Langage
    Réponses: 6
    Dernier message: 06/05/2010, 10h31
  4. transformer un fichier xml en perl
    Par knebhi dans le forum Modules
    Réponses: 3
    Dernier message: 07/05/2009, 11h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo