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 :

Changer chaines longueur variable en fixe


Sujet :

Langage Perl

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Septembre 2007
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 59
    Points : 31
    Points
    31
    Par défaut Changer chaines longueur variable en fixe
    Bien le bonsoir,

    J'ai soulevé un problème à mes collègues qui m'ont dit que la seule solution viable était de passer par le PERL. Et je dois dire que la programmation en PERL ne m'apparait pas très accessible.

    J'ai bien compris comment on remplace les caractères mais voilà, il me faut y inclure une condition qui n'est pas facile à traduire en PERL pour le néophyte que je suis. Je m'explique : j'ai un fichier de stock de créances pour lesquels chaque enregistrement est sur plusieurs lignes dont l'une d'entre elle est une suite de montants représentant chacun une période :

    1er Trim 2ème Trim 3ème Trim 4ème Trim
    120.00 150.00 80.00 40.00

    Alors mon problème est que lorsque ces montants sont positifs (>0), ils sont formatés en longueur fixe de 11 caractères et séparés entre eux par une tabulation. Mais si par malheur il y a un montant négatif, le champ est en longueur variable (celle du montant) toujours séparé d'une tabulation des autres.

    Du coup, quand je veux importer ça dans mon appli d'analyse, je suis bloqué parce que le logiciel attend de définir précisément le formatage du champ en question.

    BREF!

    je voulais faire un script PERL qui me permette de transformer le caractère "-" (symbole du négatif) par le même symbole, additionné du nombre d'espaces nécessaire pour atteindre les 11 caractères de long, variable selon le montant (ex : "-125.00" fait 7 caractères, il me faut ajouter 4 espaces).

    Si quelqu'un a une idée des fonctions à utiliser pour la condition, je suis preneur. Je sais juste basiquement remplacer le caractère mais faire la condition et le calcul des espaces manquants, là, je suis à sec

    Merci d'avance pour toute aide utile!

  2. #2
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    A priori, c'est sprintf qu'il te faut. Ce n'est pas très compliqué, et plusieurs solutions sont envisageables. En voici une grossière que tu devrais pouvoir adapter à tes besoins.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if($nombre<0)
    {
        $nombre_formate=sprintf "-%10.2f",-$nombre;
    }
    Il se trouve que par défaut, sprintf complète les nombres à taille fixe avec des espaces.
    Une question, en passant : tu es sûr que tu veux que les espaces soient entre le signe moins et les chiffres ?

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Septembre 2007
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 59
    Points : 31
    Points
    31
    Par défaut
    Ah mais pas du tout!

    Je dois avoir un truc comme ça :
    "-130.00" -> " -130.00"

    Ce qu'il y a, c'est que je ne sais pas comment lui indiquer quelle partie il doit tester pour la longueur de chaine. En fait, il faudrait que je teste tous les espaces situés entre 2 tabulations, que je vérifie si la chaine concernée contient un espace et si oui que je lui ajoute un nombre à déterminer d'espaces par la gauche. En pseudo code ça serait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Charger le fichier dans un tableau
    Variable chaine mise à 0
    Du début à la fin du fichier :
        Chaine = espace entre 2 tabulations (type chaine aussi)
        Si chaine contient "-" alors chaine=espace*(11-longueur chaine)+chaine
    Fin
    Fichier = tableau
    Alors bien sûr, il peut y avoir plein de variantes selon les instructions de PERL (à commencer par la détection des espaces entre 2 tabulations). Il faut peut-être passer par des chemins de traverse...

    Bref, j'espère m'être bien expliqué

    Merci de ton aide parce que là, c'est trop pour moi

  4. #4
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    Bon, j'avais mal lu ce que tu voulais. C'est encore plus simple.
    Je vais partir cette fois d'une ligne de ton fichier, parce que tu as l'air d'avoir du mal avec le découpage.
    Mettons que ta ligne s'appelle $ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #on découpe la ligne en utilisant split
    # plus précisément split ' ', qui est un cas particulier (voir la doc)
    my @montants = split ' ', $ligne;
    #on utilise map et sprintf pour fixer le format à 11 caractères
    @montants = map {sprintf "%11.2f", $_} @montants ;
    Et c'est tout. Tu as maintenant un tableau de chaînes formatées à 11 caractères. Tu peux alors envoyer tes montants à une application, ou utiliser join pour reformer une ligne formatée pour ton fichier, etc.
    Quelques remarques :
    Quand on lui donne à traiter une chaîne représentant un nombre, Perl ne fait aucune difficulté pour l'interpréter comme un nombre si c'est un nombre qu'il attend. D'autre part, la fonction split, utilisée avec ' ', est un cas particulier qui utilise comme critère de découpage tout ensemble consécutif de caractères blancs (tabulations, espaces, etc). C'est l'équivalent de split /\s+/. La fonction map applique une opération à chaque élément d'un tableau et renvoie les résultats sous la forme d'un tableau. Enfin, sprintf utilisé avec la chaîne de format "%11.2f" écrit un nombre sur 11 caractères, avec deux chiffres après le point, en complétant à gauche avec des espaces.

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Septembre 2007
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 59
    Points : 31
    Points
    31
    Par défaut
    Merci du coup de patte! J'essaie ça demain au bureau et je t'en dis des nouvelles

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Septembre 2007
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 59
    Points : 31
    Points
    31
    Par défaut
    Alors c'est pas encore ça

    J'ai fait plusieurs essais et dans le meilleur des cas, les chiffres étaient bien formatés mais toutes les autres infos avaient complètement disparues...

    Or en fait, mon fichier est comme un fichier spool dans lequel les enregistrements sont multilignes et les infos très diverses, y compris sur la ligne même des montants que je dois reformater.

    Alors du coup, je suis reparti dans une logique hybride et j'en suis à peu près là (il faut se souvenir que je connais pas PERL, hein) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    while ($lg=<>) {
      $ligne=$lg;
      if (index($ligne,"-")>0) {
        my @montants = split (/\t/, @ligne);
        foreach $montant (@montants) { 
          if (index($montant,"-")>0) {
            $long=length($montant);
            while($long!=11){
              $montant=" "+$montant;
              $long=$long+1;}}}
        print join(/\t/,@montants)}
      else {print "$ligne\n";}}
    L'esprit, c'est que je regarde d'abord si sur ma ligne il y a un signe "moins" (le fameux "-") qui me prévient d'un montant négatif. C'est pas terrible comme discriminant mais avec "/\t-/", ça n'a jamais voulu marcher

    Dans ce cas, je splite la ligne par les tabulations, je vérifie à nouveau la présence du "moins" mais cette fois dans le champ et j'essaie par une boucle bidon de ramener la chaine du montant à 11 caractères de long.

    Mais au résultat, ça va pas du tout parce que rien que le "join" pour remonter ma ligne ne marche pas (je me retrouve avec des lignes = "0").

    Donc mon besoin, ça serait de comprendre comment splitter proprement mes champs et les joindre plus tard aussi proprement, pourquoi il ne me repère pas la chaine "/\t-/" avec "index" alors que justement les montants négatifs sont entre 2 tabulations et enfin, même si on s'éloigne de l'esprit de PERL dans cette option, est-ce que ça tient la route?

    Merci encore

  7. #7
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Oui, il faut vérifier qu'on est bien sur une ligne où on veut effectuer des changement avant de les faire vraiment :
    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
    #!/usr/bin/env perl
    use strict; use warnings;
     
    while( <> ) {
      chomp;
      # On essaye de découper la ligne
      my @fields = split;
      # On vérifie si elle constitué entièrement de chiffres
      if( @fields == grep m/^-?\d+(\.\d*)?$/, @fields ) {
        # Si oui on la reformate en champs de 11 caractères
        # séparés par des tabulations
        print join "\t", (map {sprintf '%11.2f', $_} @fields), "\n";
      }
      else {
        # Sinon, on l'imprime telle quelle
        print "$_\n";
      }
    }
    Ca devrait à peu près faire l'affaire, essaie et dis moi si ça convient.

    --
    Jedaï

  8. #8
    Membre confirmé Avatar de iblis
    Inscrit en
    Janvier 2007
    Messages
    510
    Détails du profil
    Informations personnelles :
    Âge : 58

    Informations forums :
    Inscription : Janvier 2007
    Messages : 510
    Points : 570
    Points
    570
    Par défaut
    (Je me permets un petit complément au post de Jedaï).

    Or en fait, mon fichier est comme un fichier spool dans lequel les enregistrements sont multilignes et les infos très diverses, y compris sur la ligne même des montants que je dois reformater.
    Tu peux dans ce cas, raffiner le script de la manière suivante (j'ai supposé que les lignes à modifier commencent par une série de 4 nombres).
    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
    #!/usr/bin/perl -w                                                              
    use strict;
     
    while (<>) {
      chomp;
      if (/^(-?\d+(\.\d*)?\s*){4}/) {
        my @fields = split /\t/;
        print join "\t", (map {sprintf "%11.2f", $_} @fields[0..3]);
        print "\t", join "\t", @fields[4..$#fields] if ($#fields > 3);
        print "\n";
      }
      else {
        print $_;
      }
    }
    La fin des lignes modifiées est alors préservée.

  9. #9
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Ok, j'avais pas vu ça, dans ce cas une meilleure solution serait peut-être simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #!/usr/bin/env perl
    use strict; use warnings;
     
    while( <> ) {
      s/\t(-\d+(\.\d*)?)/sprintf "\t%11.2f", $1/ge;
      print;
    }
    Qui peut se résumer en un uniligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    perl -pe 's/\t(-\d+(\.\d*)?)/sprintf qq(\t%11.2f), $1/ge'
    --
    Jedaï

  10. #10
    Nouveau membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Septembre 2007
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 59
    Points : 31
    Points
    31
    Par défaut
    Et c'est notre dernier concurrent qui gagne le cochon gras

    Je sais pas si tu as fait exprès ( ) mais ça marche impeccable!

    La solution d'Iblis modifiait le paramétrage des champs et je n'ai pas réussi à l'arranger (forcément, je n'ai pas expliqué parfaitement le formatage de mon fichier natif)

    Par contre, la solution de Jedai correspond à ce à quoi je pensais dans mon script : ne toucher qu'au champ qui respecte les conditions.

    Donc un grand merci à tous. Je vais noter le problème comme solutionné.

    Toutefois Jedai, si ça pouvait être un effet de ta bonté, tu peux m'expliquer le script stp? Déjà, je n'arrive pas à comprendre comment tu peux ne pas mettre l'avancement dans le fichier avec "$lg" et comment on peut laisser le "print" tout seul

    Sinon, sur le script, je crois comprendre que tu cherches une chaine qui commence par une tabulation avec un "-", un champ numérique, un point, encore un numérique et que si tout ça est réuni, tu reformates le tout sur 11 caractères avec 2 chiffres après la virgule (et après "$1/ge", je sais pas ce que c'est). J'ai bon là?

  11. #11
    Membre confirmé
    Avatar de Schmorgluck
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    371
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations forums :
    Inscription : Mai 2006
    Messages : 371
    Points : 558
    Points
    558
    Par défaut
    Citation Envoyé par cyr.odi Voir le message
    Sinon, sur le script, je crois comprendre que tu cherches une chaine qui commence par une tabulation avec un "-", un champ numérique, un point, encore un numérique et que si tout ça est réunit, tu reformates le tout sur 11 caractères avec 2 chiffres après la virgule (et après "$1/ge", je sais pas ce que c'est). J'ai bon là?
    Pas loin. La solution de Jedai utilise l'opérateur de substitution en mettant du code exécutable dans la deuxième partie (je n'avais pas pensé à cette solution-là quand j'ai réfléchi à un uniligne). Le code en question est sprintf "\t%11.2f", $1. Le $1 est une variable qui contient ce qui a été reconnu dans la première parenthèse de l'expression régulière, à savoir donc le nombre, et c'est ce nombre qui va être formaté par la séquence %11.2f de la chaîne de format.
    Quant à /ge, il s'agit de la clôture de l'opérateur de substitution s/[expression régulière]/[chaîne de substitution]/, avec deux modificateurs : g, qui fait que la substitution est exercée sur toute la ligne, et e, qui permet d'évaluer du code dans l'expression, et donc d'utiliser sprintf.
    Je ne sais pas si je suis très clair, je te renvoie à la doc pour plus de détails.

  12. #12
    Nouveau membre du Club
    Homme Profil pro
    Analyste d'exploitation
    Inscrit en
    Septembre 2007
    Messages
    59
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyste d'exploitation

    Informations forums :
    Inscription : Septembre 2007
    Messages : 59
    Points : 31
    Points
    31
    Par défaut
    Je comprends qu'il faut que j'achète un bouquin

    Merci pour tes précisions et encore un grand merci à tous pour l'aide

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

Discussions similaires

  1. lire chaine de caractères à longueur variable
    Par Infra_Red dans le forum Fortran
    Réponses: 4
    Dernier message: 19/07/2014, 21h48
  2. Réponses: 0
    Dernier message: 25/01/2011, 12h34
  3. Réponses: 5
    Dernier message: 06/05/2010, 20h08
  4. couper une chaine qui a une longueur variable
    Par miketidy dans le forum Débuter
    Réponses: 3
    Dernier message: 21/10/2008, 13h28
  5. Longueur variable et longueur fixe
    Par Arola78 dans le forum Access
    Réponses: 3
    Dernier message: 22/11/2007, 17h29

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