Probleme C programmation - système de sauvegarde

hadrienlearn -  
mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   -
Bonjour,

Je poste ce problème car cela fait plusieurs heures que je tourne en rond. Je suis débutant en C, et j'essaye de développer une petit logiciel de gestion de répertoire. J'en suis arriver à la programmation du système de sauvegarde mais je bute sur un problème.

Voici la fonction :

typedef struct person
{
    char name[MAX_NAME_CHAR];
    char tel[9];
} pers;

void load_rep(pers rep[])
{
    signed char data[MAX_NAME_CHAR];
    signed char new_data;
    int id = 1;
    int rep_id = 0;
    FILE *save = fopen("save.data", "r");
    printf("Lancement de la lecture du fichier\n");
    while (1)
    {
        if (feof(save))
            break;
        new_data = fgetc(save);
        if (new_data == ' ')
            if (id == 1)
            {
                strcpy(rep[rep_id].name, data);
                id = 2;
            }
        if (id == 2)
        {
            strcpy(rep[rep_id].tel, data);
            id = 1;
            rep_id++;
        }
        else
            strncat(data, &new_data, 1);
    }
    printf("Fin de la lecture du fichier\n");
    return;
}

... et mon fichier
save.data
:
Hadrien 13215645 
Helory 13215645 
Laurent 12315465 

Lors du lancement mon programme plante. Je me demande si il n'y a pas une erreur évidente que je ne vois pas. Quoi qu'il en soit je remercie ceux qui voudrait m'aider.

Bonne soirée

2 réponses

  1. Dalfab Messages postés 638 Date d'inscription   Statut Membre Dernière intervention   102
     
    Bonjour,

    Voilà quelques problèmes que j'ai vus dans ton code:
    • feof()
      est trompeuse, elle n'indique pas si on est à la fin du fichier, elle indique si la dernière erreur est due à une fin de fichier atteinte. Lignes 11 à 13, il faudrait plutôt:

      int  int_data = fgetc(save);  // fetc return un int qui contient le caractère ou EOF
      if ( int_data == EOF )
          break;
      new_data = (char)int_data;
    • ton tableau
      data[]
      n'est jamais initialisé. Donc tenter d'utiliser
      strncat()
      ou
      strcpy()
      dessus risque de provoquer des choses curieuses, c'est peut-être la cause du plantage. Je te propose pour la ligne 3:

    char  data[MAX_NAME_CHAR] = ""; // le tableau contient une chaîne vide valide
    • la manière de récupérer les données un caractère après l'autre n'est pas la plus intuitive. D'ailleurs tu ne remets jamais
      data
      à 0, tu vas donc récupérer "Hadrien" puis "Hadrien13215645" puis "Hadrien13215645Helory" puis ....
    • de plus tu sembles supposer qu'il y a un espace dans le fichier après le
      tel[]
      alors que c'est plutôt un retour chariot!
    • ton utilisation de
      strncpy()
      est plus qu'osée, c'est une fonction prévue pour concaténer 2 chaines (donc des tableaux de
      char
      qui se terminent par un '\0') et tu lui passes l'adresse d'un unique caractère lui imposant la taille max de 1. Elle va donc ajouter un caractère mais ne mettra pas le terminateur!.
    • tu ne fermes pas ton fichier!


    Je te propose d'utiliser plutôt les fonctions
    sscanf()
    et
    fgets()
    pour extraire tes 2 mots. Ça donnerait (non testé et je suppose comme toi qu'il n'y a pas d'espaces dans les noms et comme toi je ne teste pas les éventuelles erreurs retournées par les fonctions utilisées):
    void  load_rep(pers rep[])
    {
        int  rep_id = 0;
        FILE *load = fopen( "save.data", "r" );
        printf("Lancement de la lecture du fichier\n");
        while (1)
        {
            char  data[(MAX_NAME_CHAR - 1) + 1 + (9 - 1) + 1 + 1];
            if (!fgets(data, (MAX_NAME_CHAR - 1) + 1 + (9 - 1) + 1 + 1, load))     // lire une ligne
                break;                                                   // plus rien à lire
            sscanf(data, "%s%s", rep[rep_id].name, rep[rep_id].tel);  // extraire 2 mots de la ligne lue
            rep_id++;
        }
        printf("Fin de la lecture du fichier\n");
        fclose(load);
    }
    0
  2. mamiemando Messages postés 33228 Date d'inscription   Statut Modérateur Dernière intervention   7 944
     
    Bonjour,

    En complément à la réponse donnée :
    • Il faut contrôler si le fichier a été ouvert avec succès. Si c'est le cas tu peux le lire.Tu peux repartir de cette discussion (il faut juste interrompre le programme si
      fp == NULL
      ). Cela sera plus élégant qu'une boucle while.
    • Je te recommande de partir sur un programme qui lit les lignes une par une dans un buffer, puis qui scan ce buffer avec
      sscanf
      comme le propose Dalfab.
    • En toute rigueur il faut contrôler le résultat de
      sscanf
      et vérifier que les deux valeurs ont bien été trouvées (sinon, c'est que la ligne de fichier est mal formée, et donc il faut l'ignorer).
    • Dans la réponse de Dalfab recopier directement le résultat de sscanf dans
      rep[rep_id].name
      est très cavalier car si la chaîne scanée est plus longue que
      sizeof(rep[rep_id].name)
      , alors la chaîne recopiée va déborder sur les données qui suivent et/ou provoquer une erreur de segmentation. Il faut donc contrôler au préalable que la chaîne destination est suffisamment longue. Idem pour
      rep[rep_id].tel
      .
    • Une fois le fichier lu, et seulement s'il a été ouvert avec succès, il faut le fermer.


    Autres remarques mineures :
    • Il est inutile de préciser
      signed char
      , c'est équivalent à
      char
      . En général on écrit
      unsigned char
      ou
      char
      selon qu'on veut un octet signé ou non signé.
    • Comme tes
      id
      sont positifs, les typer en
      unsigned int
      (ou
      unsigned
      en abrégé) serait avisé.


    Bonne chance
    0