Probleme C programmation - système de sauvegarde

Fermé
hadrienlearn - Modifié le 2 févr. 2022 à 14:52
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 - 2 févr. 2022 à 15:06
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
A voir également:

2 réponses

Dalfab Messages postés 706 Date d'inscription dimanche 7 février 2016 Statut Membre Dernière intervention 2 novembre 2023 101
Modifié le 2 févr. 2022 à 14:56
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
mamiemando Messages postés 33446 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 20 décembre 2024 7 812
Modifié le 2 févr. 2022 à 15:06
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