Lecture et écriture dans un fichier .txt

Résolu/Fermé
Natsuko410 Messages postés 32 Date d'inscription samedi 23 février 2019 Statut Membre Dernière intervention 11 mai 2019 - Modifié le 4 mai 2019 à 14:28
Natsuko410 Messages postés 32 Date d'inscription samedi 23 février 2019 Statut Membre Dernière intervention 11 mai 2019 - 11 mai 2019 à 12:10
Bonjour,
j'ai un projet à faire pour mes études et mon groupe et moi, on est dessus depuis un ptit temps maintenant, et là je coince depuis un moment sur quelques fonctions concernant globalement ce qui est de l'écriture et de la lecture dans un fichier. Je sais qu'il y a déjà eu des topics et tout à ce sujet et j'ai beau essayer de suivre ce qui en ressort, ça ne m'avance pas j'ai toujours le même problème et je ne sais pas comment le résoudre...

Voici en gros ce que mes fonctions doivent faire :
#ifndef THEMES_H_INCLUDED
#define THEMES_H_INCLUDED
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define FICHIER_THEMES ".\\fichiers\\themes.txt"
#define NB_THEMES_MAX 100
#define LONG_ENREG_THEME 75

struct Theme {
    int identifiant;
    char libelle[50];
};

 // crée ou vide le fichier des themes
 void initialiserFichierThemes() ;

 // renvoie le nombre de themes stockés.
 int nombreThemes();

 // compare les 2 thèmes reçus en paramètre.
 // si ils ont le même identifiant et le même libellé, la fonction renvoie 1.
 // si ce n'est pas le cas, elle renvoie 0.
 int themesIdentiques(struct Theme theme1, struct Theme theme2);

 // compare les 2 thèmes reçus en paramètre. La comparaison se fait sur les libellés.
 // si le libellé du thème 1 se trouve avant le libellé du thème 2 dans l'ordre lexicographique, elle renvoie un nombre <0.
 // si le libellé du thème 2 se trouve avant le libellé du thème 1 dans l'ordre lexicographique, elle renvoie un nombre <0.
 // si les libellés sont identiques, elle renvoie 0.
 // Utilisez la fonction strcmp()
 int comparerThemes(struct Theme theme1, struct Theme theme2);

 // ajoute un theme.
 // cette fonction doit affecter un identifiant au thème reçu en paramètre.
 // elle renvoie l'identifiant affecté au thème.
 // si erreur, elle renvoie 0.
 int ajouterTheme(struct Theme theme);


 // garnit le tableau reçu en paramètre avec les thèmes stockés.
 // renvoie le nombre de thèmes contenus dans le tableau
 int listerThemes(struct Theme themes[]);

 // recherche si un thème existe. Recherche sur base de l'identifiant.
 // renvoie 1 si le thème est stocké.
 // renvoie 0, si pas présent.
 int themeExiste(int identifiant);

 // recherche le libellé du thème dont on reçoit l'identifiant en paramètre
 // si le thème identifié est présent, on recopie son libellé dans le paramètre libelle. On renvoie 1.
 // si le thème n'est pas présent, on affecte une chaine vide au paramètre libelle. On renvoie 0.
 int rechercherLibelleTheme(int identifiant, char libelle[]);

 // modifie le libellé du thème identifié par l'identifiant reçu en paramètre.
 // si le thème est présent, son libellé est modifié. La valeur 1 est renvoyée.
 // si aucun thème ne correspond à cet identifiant, la fonction renvoie 0.
 int modifierTheme(int identifiant, char nouveauLibelle[]);

 // Supprime un thème. L'identifiant du thème à supprimer est passé en paramètre.
// Suppression "logique". Les données ne sont supprimées "physiquement").
// si l'identifiant reçu en paramètre correspond à un thème stocké, celui-ci est supprimé et elle renvoie 1.
// si l'identifiant reçu en paramètre ne correspond à aucun thème stocké, elle renvoie 0.
int supprimerTheme(int identifiant);

// supprime pysiquement le contenu du fichier des thèmes.
// renvoie le nombre de thèmes qui ont été supprimés.
int supprimerTousLesThemes();

 // garnit le tableau reçu en paramètre avec les thèmes triés de façon ascendante sur leur libellé.
 // renvoie le nombre de thèmes contenus dans le tableau
 int listerThemesTries(struct Theme themes[]);

#endif // THEMES_H_INCLUDED


Et voici ce que moi j'ai codé pour le moment :
void initialiserFichierThemes()
{
    /* Declaration */
    FILE *fout=NULL;

    /* Ouverture du fichier */
    fout=(fopen(FICHIER_THEMES,"wt"));

    /* Fermeture du fichier */
    fclose(fout);
}

int nombreThemes()
{
    /* Declaration */
    int nbThemes=0;

    struct Theme theme;

    FILE *fin=NULL;

    /* Ouverture du fichier */
    fin=(fopen(FICHIER_THEMES,"rt"));

    /* Traitement */
    if ((fscanf(fin,"%d %s",&theme.identifiant,&theme.libelle[0]))==2)
    {
        nbThemes++;
        while ((fscanf(fin,"%d %s",&theme.identifiant,&theme.libelle[0]))==2)
        {
            nbThemes++;
        }
    }

    /* Fermeture du fichier */
    fclose(fin);

    return(nbThemes);
}

int themesIdentiques(struct Theme theme1, struct Theme theme2)
{
    /* Traitement */
    if ((strcmp(theme1.libelle,theme2.libelle)==0) && (theme1.identifiant==theme2.identifiant)) return(1); // Les themes sont identiques

    return(0); // Les themes ne sont pas identitques
}
int comparerThemes(struct Theme theme1, struct Theme theme2)
{
    /* Declaration */
    int car;

    /* Traitement */
    car=strncmp(theme1.libelle, theme2.libelle, 1); // sauvegarde le resultat de strncmp

    if(car < 0) return (-1); // Le libelle du theme 1 se trouve avant le libelle du theme 2 dans l'ordre lexicographique
    else if(car > 0) return (-2); // Le libelle du theme 2 se trouve avant le libelle du theme 1 dans l'ordre lexicographique
    else return (0); // Les libelles sont identiques
}

int ajouterTheme(struct Theme theme)
{
    int erreur=0;
    int i=1;
    char ligne[LONG_ENREG_THEME];
    struct Theme temp;

    FILE *fout=NULL;


    if((fout=fopen(FICHIER_THEMES,"at+")))
    {
        fseek(fout,0,SEEK_END);
        if((ftell(fout))!=0)
        {
            fseek(fout,0,SEEK_SET);
            while(((fgets(ligne,LONG_ENREG_THEME,fout))!=NULL) && (erreur==0))
            {
                if((sscanf(ligne,"%d %s",&temp.identifiant,temp.libelle))==2)
                {
                    i++;
                }
                else erreur=1;
            }
            if((fprintf(fout,"%d %s\n",i,theme.libelle))>0) return(i);
            else erreur=2;
        }
        else
        {
            fseek(fout,0,SEEK_SET);
            if((fprintf(fout,"%d %s\n",i,theme.libelle))>0) return(i);
            else erreur=2;
        }

    } else erreur=1;

    switch(erreur){
    case 1: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ouverture du fichier themes.txt\n\n\n\n\n"); break;
    case 2: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ecriture dans le fichier themes.txt\n\n\n\n\n"); break;
    case 3: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de la lecture dans le fichier themes.txt\n\n\n\n\n"); break;
    //case 4: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors du positionnemment dans le fichier themes.txt\n\n\n\n\n"); break;
    }

    return(0);
}

int listerThemes(struct Theme themes[])
{
    int tailleFichier;
    int nbThemeTab=0;
    int erreur=0;

    FILE* fin=NULL;

    if ((fin=fopen(FICHIER_THEMES,"rt+")))
    {

        fseek(fin,0,SEEK_END);
        tailleFichier=ftell(fin);
        fseek(fin,0,SEEK_SET);

        nbThemeTab=tailleFichier/LONG_ENREG_THEME;

    } else erreur=1;

    switch(erreur)
    {
    case 1: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ouverture du fichier themes.txt\n\n\n\n\n"); break;
    //case 2: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'écriture dans le fichier themes.txt\n\n\n\n\n"); break;
    //case 3: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de la lecture dans le fichier themes.txt\n\n\n\n\n"); break;
    //case 4: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors du positionnemment dans le fichier themes.txt\n\n\n\n\n"); break;
    }

    return(nbThemeTab);
}

int themeExiste(int identifiant)
{
    /* Declaration */
    int erreur=0; // variable pour stocker les resultats des tests en cas d'erreur

    FILE *fin=NULL; // Pointeur fout de type FILE

    struct Theme theme;

    /* Ouverture du fichier */
    if ((fin=fopen(FICHIER_THEMES,"rt"))); // Ouverture du fichier en mode ecriture avec un test pour verifier que le fichier s'ouvre correctement
    else erreur=1; // S'il ne s'ouvre pas


    /* Traitement */
    if(fscanf(fin,"%d %s",&theme.identifiant,&theme.libelle[0]))
    {
        while ((!feof(fin)) && (erreur!=3))
        {
            if (identifiant==theme.identifiant)
            {
                return(1);
            }
            fscanf(fin,"%d %s",&theme.identifiant,&theme.libelle[0]);
        }
    }

    /* Fermeture du fichier */
    switch(erreur)
    {
    case 1: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ouverture du fichier themes.txt\n\n\n\n\n"); break;
    //case 2: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'écriture dans le fichier themes.txt\n\n\n\n\n"); break;
    case 3: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de la lecture dans le fichier themes.txt\n\n\n\n\n"); break;
    //case 4: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors du positionnemment dans le fichier themes.txt\n\n\n\n\n"); break;
    }

    return(0);
}

int rechercherLibelleTheme(int identifiant, char libelle[])
{
    return -1;
}

int modifierTheme(int identifiant, char nouveauLibelle[])
{
    return -1;
}

int supprimerTheme(int identifiant)
{
    int erreur=0;
    char ligne[LONG_ENREG_THEME];

    FILE *fout=NULL;
    struct Theme temp;

    fout=(fopen(FICHIER_THEMES,"r+"));

    while(((fgets(ligne,LONG_ENREG_THEME,fout))!=NULL) && (erreur==0))
    {
        if((sscanf(ligne,"%d %s",&temp.identifiant,temp.libelle))==2)
        {
            if(temp.identifiant==identifiant)
            {
                strcpy(temp.libelle,"*-*");
                fprintf(fout,"%d %s",temp.identifiant,temp.libelle);
                return(1);
            }
        }
        else erreur=1;
    }

    /* Fermeture du fichier */
    switch(erreur)
    {
    case 1: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ouverture du fichier themes.txt\n\n\n\n\n"); break;
    case 2: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'écriture dans le fichier themes.txt\n\n\n\n\n"); break;
    case 3: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de la lecture dans le fichier themes.txt\n\n\n\n\n"); break;
    //case 4: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors du positionnemment dans le fichier themes.txt\n\n\n\n\n"); break;
    }

    return(0);
}

int supprimerTousLesThemes()
{

   return -1;
}

int listerThemesTries(struct Theme themes[])
{
    return -1;
}


Et voici ce qui en résultent dans mon fichier :


Je tiens aussi à préciser, que je sais qu'à certains endroits j'ai commencé à traiter les erreurs mais que ce n'est pas fini :)
Voilà, je remercie d'avance tous ceux qui prendront le temps de m'aider ^^


Configuration: Windows / Chrome 73.0.3683.103
A voir également:

1 réponse

[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
6 mai 2019 à 17:35
Bonjour Natsuko410,

j'ai toujours le même problème

Lequel ?

Sinon, quelques réflexions en vrac :

1.

// compare les 2 thèmes reçus en paramètre. La comparaison se fait sur les libellés.
 // si le libellé du thème 1 se trouve avant le libellé du thème 2 dans l'ordre lexicographique, elle renvoie un nombre <0.
 // si le libellé du thème 2 se trouve avant le libellé du thème 1 dans l'ordre lexicographique, elle renvoie un nombre <0.
 // si les libellés sont identiques, elle renvoie 0.
 // Utilisez la fonction strcmp()
 int comparerThemes(struct Theme theme1, struct Theme theme2);


je pense qu'il y a une erreur dans le descriptif et qu'on devrait lire "si le libellé du thème 2 se trouve avant le libellé du thème 1 dans l'ordre lexicographique, elle renvoie un nombre >0."

Par rapport à ton code de comparerThemes(), tu n'utilises pas strcmp() alors qu'on te demande de le faire.

Quand je compare une chaîne "themeA" et "themeB", il me semble que "themeA" est avant "themeB" dans l'ordre lexicographique, et pas que les chaînes sont identiques.

2.

Tes boucles de lecture du contenu des fichiers sont inutilement compliquées.

Par exemple, ceci suffit pour compter les thèmes, avec, en plus un contrôle du succès de l'ouverture et une fermeture du fichier s'il a été ouvert :

    fin = fopen(FICHIER_THEMES,"r");

    /* Traitement */
    if (fin) {
        while (fscanf(fin,"%d %s",&theme.identifiant,theme.libelle) == 2)
            nbThemes++;
        fclose(fin);
    }


Quand tu ouvres un fichier en mode d'ajout avec fopen() ("a" pour append, pas "at+"), tu n'as pas à te positionner dans le fichier pour écrire à la fin. Les opérations d'écriture se feront directement à la fin.

Je ne vois nulle part dans le code de ajouterTheme() que tu retournes l'identifiant du thème ajouté.

Je ne pense pas qu'il faille déterminer l'identifiant par rapport au nombre d'éléments... mais on a déjà eu cette discussion

Pour supprimer un thème du fichier, à mon sens, tu dois :
- ouvrir le fichier en lecture contenant les thèmes (themes.txt)
- ouvrir en écriture un fichier temporaire (temp.txt)
- lire dans le fichier themes.txt chaque ligne et l'écrire dans temp.txt sauf si la ligne contient ce que tu dois supprimer
- fermer themes.txt et temp.txt avec fclose()
- effacer themes.txt et renommer temp.txt en themes.txt

Dal
1
Natsuko410 Messages postés 32 Date d'inscription samedi 23 février 2019 Statut Membre Dernière intervention 11 mai 2019
Modifié le 6 mai 2019 à 19:18
Tout d'abord, merci pour ta réponse ^^
Et en fait, mon problème c'est ajouterTheme qui comme on peut le voir sur le screen de mon fichier ne fait pas ce qu'elle est censé faire, elle met le même identifiant aux différents thème et en plus, les recopies je ne sais pas combien de fois.
Pour la fonction comparerTheme, en effet, j'ai pas vérifié après celui qui l'a faite et du coup j'avais pas vu l'erreur, merci ^^
Pour ajouterTheme, je retourne i que j'ai incrémenter en lisant les enregistrements. Et pour l'identifiant, je vois pas comment je peux faire pour avoir l'identifiant correct parce qu'en fait, c'est vrai que j'ai oublié de le préciser je m'en excuse, mais je suis obligé de fonctionner de manière, comment dire... logique càd que je ne peux pas réellement supprimer un thème, je peux juste le remplacer par qqch comme par exemple : la chaine de caractère "DELETED" et dans nombreTheme j'identifie si c'est différent ou pas de cette même chaîne pour connaitre réellement le nombre de thème
Je sais pas si j'ai été super clair mais en gros c'est ça ^^
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
Modifié le 6 mai 2019 à 19:18
mon problème c'est ajouterTheme qui comme on peut le voir sur le screen de mon fichier ne fait pas ce qu'elle est censé faire, elle met le même identifiant aux différents thème et en plus, les recopies je ne sais pas combien de fois.

Peux-tu poster un exemple de code appelant cette fonction et créant un fichier erroné ?

je ne peux pas réellement supprimer un thème, je peux juste le remplacer par qqch comme par exemple : la chaine de caractère "DELETED"

OK, si c'est comme cela que tu dois fonctionner, mais il n'empêche que tu dois quand même passer par la création d'un fichier temporaire, le déroulement étant alors :

- ouvrir le fichier en lecture contenant les thèmes (themes.txt)
- ouvrir en écriture un fichier temporaire (temp.txt)
- lire dans le fichier themes.txt chaque ligne et l'écrire dans temp.txt sauf si la ligne contient ce que tu dois supprimer, auquel cas on écrit "DELETED"
- fermer themes.txt et temp.txt avec fclose()
- effacer themes.txt et renommer temp.txt en themes.txt
0
Natsuko410 Messages postés 32 Date d'inscription samedi 23 février 2019 Statut Membre Dernière intervention 11 mai 2019
7 mai 2019 à 19:00
Voilà je pense que c'est ça que tu m'as demandé ^^

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define FICHIER_THEMES ".\\fichiers\\themes.txt"
#define NB_THEMES_MAX 100
#define LONG_ENREG_THEME 75

struct Theme {
    int identifiant;
    char libelle[50];
};

int ajouterTheme(struct Theme theme);

int main()
{
    struct Theme exemple1;
    struct Theme exemple2;
    struct Theme exemple3;

    // envoie dans le fichier themes.txt de l'exemple 1
    strcpy(exemple1.libelle,"cinema");
    exemple1.identifiant=-1;
    ajouterTheme(exemple1);

    // envoie dans le fichier themes.txt de l'exemple 2
    strcpy(exemple2.libelle,"musique");
    exemple2.identifiant=-1;
    ajouterTheme(exemple2);


    // envoie dans le fichier themes.txt de l'exemple 3
    strcpy(exemple3.libelle,"sport");
    exemple3.identifiant=-1;
    ajouterTheme(exemple3);

    return(0);
}

int ajouterTheme(struct Theme theme)
{
    int erreur=0;

    FILE *fout=NULL;

    fout=fopen(FICHIER_THEMES,"a+");

    if(fout)
    {
        theme.identifiant=((ftell(fout))/sizeof(theme))+1;
        fprintf(fout,"%d %s\n",theme.identifiant,theme.libelle);

    } else erreur=1;

    switch(erreur){
    case 0: fprintf(stdout, "\n\n\n\n\n\t\t\t\tEcriture dans le fichier reussie !\n\n\n\n\n"); break;
    case 1: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ouverture du fichier themes.txt\n\n\n\n\n"); break;
    case 2: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de l'ecriture dans le fichier themes.txt\n\n\n\n\n"); break;
    case 3: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors de la lecture dans le fichier themes.txt\n\n\n\n\n"); break;
    //case 4: fprintf(stderr, "\n\n\n\n\n\t\t\t\tErreur lors du positionnemment dans le fichier themes.txt\n\n\n\n\n"); break;
    }

    return(theme.identifiant);
}



Voilà comment le projet est organisé :



Et voilà ce que ça donne :


Je sais pas comment ça se fait mais j'ai plus exactement le même problème que dans le "VRAI" projet, mais toujours est-il que je sais pas comment faire pour avoir le bon identifiant ^^
Encore une fois, merci pour ta réponse ça m'aide beaucoup, j'avance petit à petit mais j'avance :D
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
Modifié le 7 mai 2019 à 19:28
OK, merci

Tu pouvais juste faire un main avec un
#include "themes.h"
, cela aurait suffi, mais là c'est encore plus clair et c'est une bonne façon de circonscrire le problème pour toi aussi :-)

1.

Donc voilà ce qui la documentation de la fonction standard ftell() :

http://www.cplusplus.com/reference/cstdio/ftell/

long int ftell ( FILE * stream );

Get current position in stream
Returns the current value of the position indicator of the stream.

For binary streams, this is the number of bytes from the beginning of the file.

For text streams, the numerical value may not be meaningful but can still be used to restore the position to the same position later using fseek (if there are characters put back using ungetc still pending of being read, the behavior is undefined).


Autrement dit, pour les fichiers textes, tu ne peux pas te fier à cette valeur pour déterminer quoi que ce soit, et encore moins en faisant une division entière avec la taille en octets en mémoire d'une struct, qui n'a de sens que pour des données binaires si tu écrivais la totalité du contenu binaire de la mémoire (ce qui n'est pas le but recherché).

2.

Compte tenu des informations que tu as fournies précédemment, et notamment du fait qu'une ligne n'est jamais vraiment retirée du fichier, tu peux établir une stratégie simple d'attribution d'identifiant : l'identifiant d'un thème est le numéro de la ligne sur laquelle le thème est enregistré, en commençant par 1.

Cette stratégie peut être, par exemple :

- si tu ne gères pas le contenu en mémoire, de faire une première passe sur le fichier en lecture pour dénombrer le nombre de lignes stockées, y compris les lignes "DELETED", afin de déterminer le prochain numéro
- si tu gères le contenu en mémoire, de conserver l'information sur le nombre de lignes en mémoire vive du programme et d'incrémenter ce numéro lorsque tu ajoutes un thème
0
[Dal] Messages postés 6194 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 11 octobre 2024 1 092
7 mai 2019 à 19:32
Une question : ne devrais-tu pas vérifier avant d'ajouter un thème, que ce libellé n'existe pas déjà dans la base avec un autre identifiant ?
0