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

C Discussion :

Lire fichier formatté


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Avatar de odsen.s
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2006
    Messages
    269
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2006
    Messages : 269
    Par défaut Lire fichier formatté
    Bonsoir à tous,

    Pour m'entraîner en C, je réalise en ce moment un programme permettant de tenir des comptes, des budgets.

    Les fichiers de budget sont formattés comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    date,montant,motif,nouveau_solde
    Chaque ligne contient une opération (un crédit ou un débit).
    Les nouvelles opérations sont ajoutées au début du fichier

    Exemple de fichier :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    05/01/2007,-50.99,achat telephone portable,449.01
    02/01/2007,200.0,etrennes,500.0
    30/12/2006,-25.0,cadeau pour tante yvonne,300.0
    Je suis en train de créer une fonction qui lit une operation et enregistre les valeurs dans une structure déclarée comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    typedef struct Operation
    {
       char s_date[LONG_DATE];
       double montant;
       char s_motif[LONG_MOTIF];
       double nouveau_solde;
    } Operation;
    Voici le prototype de ma fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    /* n_op est le numero de l'operation, 1 etant la plus recente */
    int lire_operation_fmt(FILE *fp_compte, size_t n_op, Operation *op);
    Je me demande comment lire l'entrée formattée.
    J'ai tout de suite rejeté fscanf() car si le fichier est modifié incorrectement de l'extérieur, le programme donne un comportement indéfini, c'est bien ça ?

    Je me suis alors décidé à lire la ligne concernée avec fgets(), puis de tout faire à la main. Mais le travail devient fastidieux. Il faut copier, jusqu'à rencontrer une virgule, le contenu de la ligne dans une nouvelle variable, qu'il faut convertir en double si nécessaire. Le nombre d'erreurs à gérer me paraît impressionant (ligne inexistante, format incorrect de la ligne, chaines trop longues, nombres impossibles à convertir en double...).

    Auriez-vous une autre méthode pour lire ces valeurs séparées par des virgules , ou celle que je me suis proposée est la seule possible ?

    Par avance, merci

  2. #2
    Membre émérite
    Profil pro
    Eleveur de cornichons
    Inscrit en
    Juin 2002
    Messages
    1 074
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Eleveur de cornichons
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2002
    Messages : 1 074
    Par défaut
    Salut Benoit

    Je dirais qu'il est très difficile de gérer les erreurs éventuelles de syntaxe dans le fichier texte. La meilleure solution serait que ce soit ton programme C qui crée le fichier. En clair, tu ne rentres pas en dur les données mais depuis ton programme. Comme ça, tu es sûr...
    Une autre solution, plus correcte à mon avis, est de changer la forme de ton fichier texte et de passer par du XML. Comme ça, tu peux être sûr que ton fichier est bien "construit". D'ailleurs, beaucoup de programmes utilisent des fichiers XML pour les configurations pour avoir une forme "sûre" au cas où l'utilisateur casse tout
    Pour lire ton fichier, il faudra passer par des bibliothèques externes qui gèrent le XML.

    Nas'

  3. #3
    Membre Expert
    Avatar de Gruik
    Profil pro
    Développeur Web
    Inscrit en
    Juillet 2003
    Messages
    1 566
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Juillet 2003
    Messages : 1 566
    Par défaut
    Salut,

    Je ferais comme toi egalement, fgets + analyse manuelle.
    Ta gestion d'erreur peut être plus simple, ça depend du niveau de robustesse que tu te fixes. Tu peux te dire "bein si une ligne est pas correcte on la zappe et puis c'est marre", ya pas forcement besoin de faire un traitement particulier pour chaque erreur rencontrée.

    Les choses que je prendrais en consideration :
    * definir une longueur max de ligne et decider quoi faire si une ligne est trop grande
    * faire bien attention à ce qu'il n'y ait pas le separateur (la virgule) dans les valeurs, lors de l'insertion de données.

  4. #4
    Membre éclairé
    Avatar de odsen.s
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2006
    Messages
    269
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2006
    Messages : 269
    Par défaut
    Bonsoir à tous les deux,
    Je dirais qu'il est très difficile de gérer les erreurs éventuelles de syntaxe dans le fichier texte. La meilleure solution serait que ce soit ton programme C qui crée le fichier. En clair, tu ne rentres pas en dur les données mais depuis ton programme. Comme ça, tu es sûr...
    C'est bien ce que je comptais faire, mais il n'est pas exclu qu'un utilisateur décide de modifier le fichier depuis l'extérieur...

    Une autre solution, plus correcte à mon avis, est de changer la forme de ton fichier texte et de passer par du XML.
    Pourquoi pas. Je n'ai jamais formatté en XML, je pense que je vais commencer par regarder par là. Et puis ça sera toujours instructif.

    Si je ne trouve pas mon bonheur avec le XML, je repasserai à ma première méthode, comme me le conseille gruik.

    Je vous tiens au courant

  5. #5
    Membre éclairé
    Avatar de odsen.s
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2006
    Messages
    269
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2006
    Messages : 269
    Par défaut
    J'ai finalement choisi de conserver ma méthode initiale, je vais attendre un peu pour le XML
    En suivant tes conseils, gruik, ça se passe bien.

    Merci à tous les deux

  6. #6
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    Pour info, quand tu dis :

    Citation Envoyé par odsen.s
    ....
    . Mais le travail devient fastidieux. Il faut copier, jusqu'à rencontrer une virgule, le contenu de la ligne dans une nouvelle variable, qu'il faut convertir en double si nécessaire. Le nombre d'erreurs à gérer me paraît impressionant (ligne inexistante, format incorrect de la ligne, chaines trop longues, nombres impossibles à convertir en double...).
    ...

    Fais-voir ton code, car ça ne me semble pas si fastidieux que ça...

  7. #7
    Membre éclairé
    Avatar de odsen.s
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2006
    Messages
    269
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2006
    Messages : 269
    Par défaut
    Salut,

    J'ai fait ça (sans doute perfectible, je n'ai pas encore géré certaines erreurs) :
    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    int lire_operation_fmt(FILE *fp_compte, size_t n_op, Operation *p_op)
    {
       size_t i;
       int err = OK;
       char s_ligne[LONG_LIGNE] = {0};
     
       /* on se rend a la bonne ligne */
     
       fseek(fp_compte, 0, SEEK_SET);
     
       for(i = 0; i < n_op; i++)
       {
          if(fgets(s_ligne, sizeof s_ligne, fp_compte) == NULL)
          {
             err = OP_INEXISTANTE;
             break;
          }
       }
     
       if(err != OP_INEXISTANTE)
       {
          if(verif_fmt_ligne(s_ligne, sizeof s_ligne) == OK)
          {
             /* copie des valeurs */
     
             size_t j;
     
             for(i = j = 0; s_ligne[i] != ','; i++, j++)
             {
                p_op->s_date[j] = s_ligne[i];
             }
     
             p_op->s_date[j] = 0;
             i++;
     
             for(j = 0; s_ligne[i] != ','; i++, j++)
             {
                p_op->s_montant[j] = s_ligne[i];
             }
     
             p_op->s_montant[j] = 0;
             i++;
     
             for(j = 0; s_ligne[i] != ','; i++, j++)
             {
                p_op->s_motif[j] = s_ligne[i];
             }
     
             p_op->s_motif[j] = 0;
             i++;
     
             for(j = 0; s_ligne[i] != '\n'; i++, j++)
             {
                p_op->s_solde[j] = s_ligne[i];
             }
     
             p_op->s_solde[j] = 0;
     
             /* conversion en double pour montant et solde */
     
             char *p_conversion = NULL;
             errno = 0;
     
             p_op->montant = strtod(p_op->s_montant, &p_conversion);
     
             if(p_conversion != NULL)
             {
                 if(*p_conversion == 0)
                 {
                     /* valeur trop grande */
     
                     if(errno == ERANGE)
                     {
                         err = ERR_VAL;
                     }
     
                     else
                     {
                         p_conversion = NULL;
                         errno = 0;
     
                         p_op->solde = strtod(p_op->s_solde, &p_conversion);
     
                         if(p_conversion != NULL)
                         {
                             if(*p_conversion == 0)
                             {
                                 /* valeur trop grande */
     
                                 if(errno == ERANGE)
                                 {
                                     err = ERR_VAL;
                                 }
                             }
     
                             else
                             {
                                 err = ERR_CONV;
                             }
                         }
                     }
                 }
     
                 else
                 {
                     err = ERR_CONV;
                 }
             }
          }
     
          else
          {
             err = FORMAT_KO;
          }
       }
     
       return err;
    }
     
    int verif_fmt_ligne(const char* s_ligne, size_t t)
    {
       /* format correct <=> (NB_VIRGULES) virgules dans la ligne */
     
       int nb_virgules = 0;
       size_t i;
     
       for(i = 0; i < t; i++)
       {
          if(s_ligne[i] == '\0')
          {
             break;
          }
     
          else if(s_ligne[i] == ',')
          {
             nb_virgules++;
          }
       }
     
       return nb_virgules - NB_VIRGULES;
    }
    Avec :
    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
    #define LONG_LIGNE 128
    #define LONG_DATE 16
    #define LONG_MONTANT 32
    #define LONG_MOTIF 32
    #define LONG_SOLDE 32
     
    #define NB_VIRGULES 3
     
    enum
    {
       OK,
       FORMAT_KO,
       OP_INEXISTANTE,
       ERR_LONG,
       ERR_CONV,
       ERR_W
    };
     
    typedef struct Operation
    {
       char s_date[LONG_DATE];
       char s_montant[LONG_MONTANT];
       double montant;
       char s_motif[LONG_MOTIF];
       char s_solde[LONG_SOLDE];
       double solde;
    } Operation;
    (ERR_VAL est définie dans un autre fichier d'en-tête)

  8. #8
    Membre émérite Avatar de BlaireauOne
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    492
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mars 2007
    Messages : 492
    Par défaut
    Citation Envoyé par odsen.s
    Bonsoir à tous,

    .../...
    Auriez-vous une autre méthode pour lire ces valeurs séparées par des virgules , ou celle que je me suis proposée est la seule possible ?

    Par avance, merci

    Jette un coup d'oeil sur : http://c.developpez.com/faq/vc/?page...ons#MakeStrtok

    Comment faire une extraction dans une CString avec des séparateurs ?

    strtok est une fonction C : http://man.developpez.com/man3/strtok.3.php

  9. #9
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    c'est bien ce que je pensais

    tu sais que tu peux chercher un caractère directement avec la fonction strchr ?

    Et que tu peux faire de l'arithmétique de pointeurs ?

    Du coup, pas besoin de passer toi-même à travers toutes ces boucles .

    Exemple :

    soit la chaîne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    30/12/2006,-25.0,cadeau pour tante yvonne,300.0
    Une fois la ligne lue par fgets, voici comment t'en sortir proprement :

    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
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    char  *p = NULL, *p0=NULL ;
    char   date[11] ;
    char  *objet=NULL ;
    double montant ;
    double solde ;
     
    ........
    ........
     
       /* Lit la date */
     
       p = strchr ( Ligne, ',' );  /* Va détecter la première virgule */
       if ( p == NULL )
         {
            fprintf ( stderr, " Erreur : mauvaise ligne");
            return ERROR ;
         }
       *p = '\0' ;
       strcpy ( Date, Ligne );
     
       p0 = p + 1 ;
     
       /* Lit le montant */
      p = strchr ( p0, ',');
      if ( p == NULL )
       {
           fprintf ( stderr, " Erreur : mauvaise ligne");
           return ERROR ;
        }
      *p = '\0'; 
       sscanf ( p0, "%lf", &montant );
     
       p0 = p + 1 ;
     
      /* Lit l'objet */
       p = strchr ( p0, ',');
       if ( p == NULL )
         {
             fprintf ( stderr, " Erreur : mauvaise ligne");
             return ERROR ;
         }
      *p = '\0'; 
       objet = malloc ( (strlen(p0)+1) );
       if ( objet == NULL )
        {
           fprintf ( stderr, "\n ERREUR d'allocation");
           return ERROR ;
        }
       strcpy ( objet, p0 );
     
       p0 = p + 1 ;
     
       /* Lit le solde */
       p = strchr ( p0, ',');
       if ( p == NULL )
        {
           fprintf ( stderr, " Erreur : mauvaise ligne");
           return ERROR ;
        }
       *p = '\0'; 
       sscanf ( p0, "%lf", &solde );
    ......
    .....
    }
    Que tu peux ensuite simplifier en :

    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
    53
    54
    55
    56
    57
     
    char  *p = NULL, *p0=NULL ;
    char   date[11] ;
    char  *objet=NULL ;
    double montant ;
    double solde ;
    int    count=0 ;
     
    ........
    ........
     
      p0 = Ligne ;
     
      while ( (p = strchr ( p0, ',' )) != NULL ) 
         {
             switch ( count )
               {
                   case 0 :  /* Lit la date */
                       *p = '\0' ;
                        strcpy ( Date, p0 );
                        break ;
     
                   case 1 :   /* Lit le montant */
                       *p = '\0'; 
                        sscanf ( p0, "%lf", &montant );
                        break ;
     
                   case 2 :  /* Lit l'objet */
                      *p = '\0'; 
                       objet = malloc ( (strlen(p0)+1) );
                       if ( objet == NULL )
                         {
                            fprintf ( stderr, "\n ERREUR d'allocation");
                            return ERROR ;
                         }
                       strcpy ( objet, p0 );
                       break ;
     
                   case 3 :   /* Lit le solde */
                      *p = '\0'; 
                       sscanf ( p0, "%lf", &solde );
                       break ;
               }
     
             p0 = p + 1 ;
             count = count + 1 ;
        }
     
      if ( count != 4 )
        {
            fprintf ( stderr, " Erreur : mauvaise ligne");
             return ERROR ;
        }
     
    ......
    .....
    }
    ou comme le disait BlaireauOne, avec strtok ;

    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
    53
     
    char  *p = NULL, *p0=NULL ;
    char   date[11] ;
    char  *objet=NULL ;
    double montant ;
    double solde ;
    int    count=0 ;
     
    ........
    ........
     
      p0 = Ligne ;
     
      while ( (p = strtok ( p0, "," )) != NULL ) 
         {
             switch ( count )
               {
                   case 0 :  /* Lit la date */
                        strcpy ( Date, p );
                        p0 = NULL ;  /* pour les passages successifs de strtok */
                        break ;
     
                   case 1 :   /* Lit le montant */
                        sscanf ( p, "%lf", &montant );
                        break ;
     
                   case 2 :  /* Lit l'objet */
                       objet = malloc ( (strlen(p)+1) );
                       if ( objet == NULL )
                         {
                            fprintf ( stderr, "\n ERREUR d'allocation");
                            return ERROR ;
                         }
                       strcpy ( objet, p );
                       break ;
     
                   case 3 :   /* Lit le solde */
                       sscanf ( p, "%lf", &solde );
                       break ;
               }
     
             count = count + 1 ;
        }
     
      if ( count != 4 )
        {
            fprintf ( stderr, " Erreur : mauvaise ligne");
             return ERROR ;
        }
     
    ......
    .....
    }


  10. #10
    Membre éclairé
    Avatar de odsen.s
    Profil pro
    Étudiant
    Inscrit en
    Octobre 2006
    Messages
    269
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2006
    Messages : 269
    Par défaut
    Waow, ça fait tout de suite plus propre !
    J'étudie tout ça, merci beaucoup

  11. #11
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    euh.. juste un petit oubli..

    Pour avoir le dernier champ, avec strtok, il faudra faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     while ( (p = strtok ( p0, ",\n" )) != NULL )
    et pareil avec strchr :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
      while ( ((p = strchr ( p0, ',' )) != NULL) || ((p=strchr(p0, '\n')) != NULL)  )
    Désolé..

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

Discussions similaires

  1. Lire un fichier formatter
    Par rzayani dans le forum Linux
    Réponses: 3
    Dernier message: 29/04/2008, 15h18
  2. [W3C] Lire fichier *.mid sans plugin midi ?
    Par Lareine dans le forum Balisage (X)HTML et validation W3C
    Réponses: 12
    Dernier message: 14/11/2005, 13h23
  3. lire fichier word
    Par benoit70 dans le forum VBA Word
    Réponses: 2
    Dernier message: 26/09/2005, 09h11
  4. Réponses: 2
    Dernier message: 11/09/2005, 05h25

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