4 votes

Lecture du fichier et remplissage de la structure

J'ai une structure avec la définition suivante :

typedef struct myStruct{
    int a;
    char* c;
    int f;
} OBJECT;

Je suis capable de remplir cet objet et de l'écrire dans un fichier. Cependant, je ne parviens pas à lire la valeur char* c qu'il contient... lorsque j'essaie de la lire, il me donne une erreur de segmentation. Y a-t-il un problème avec mon code ?

//writensave.c

#include "mystruct.h"
#include <stdio.h>
#include <string.h>

#define p(x) printf(x)

int main()
{
    p("Creating file to write...\n");
    FILE* file = fopen("struct.dat", "w");
    if(file == NULL)
    {
        printf("Error opening file\n");
        return -1;
    }

    p("creating structure\n");
    OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
    myObj->a = 20;
    myObj->f = 45;
    myObj->c = (char*)calloc(30, sizeof(char));
    strcpy(myObj->c, 
        "This is a test");
    p("Writing object to file...\n");
    fwrite(myObj, sizeof(OBJECT), 1, file);
    p("Close file\n");
    fclose(file);
    p("End of program\n");
    return 0;       
}

Voici comment j'essaie de le lire :

//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{   
    FILE* file = fopen("struct.dat", "r");
    char* buffer;
    buffer = (char*) malloc(sizeof(OBJECT));
    if(file == NULL)
    {
        p("Error opening file");
        return -1;
    }

    fread((void *)buffer, sizeof(OBJECT), 1, file);
    OBJECT* obj = (OBJECT*)buffer;
    printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
        obj->a,
        obj->f,
        obj->c);
    fclose(file);
    return 0;
}

2voto

dash-tom-bang Points 9384

Lorsque vous écrivez votre objet, vous écrivez la valeur du pointeur dans le fichier au lieu de l'information pointée.

Ce que vous devez faire, c'est pas Ne vous contentez pas de fwrite/fread votre structure entière, mais faites plutôt un champ à la fois. fwrite le a et le f comme vous le faites avec l'objet, mais ensuite vous devez faire quelque chose de spécial avec la chaîne. Essayez fwrite/fread de la longueur (non représentée dans votre structure de données, ce n'est pas grave) et ensuite fwrite/fread du tampon de caractères. A la lecture, vous devrez l'allouer, bien sûr.

2voto

bta Points 22525

Votre premier exemple de code semble supposer que les chaînes de caractères ne seront pas supérieures à 30 caractères. Si c'est le cas, la solution la plus simple est probablement de redéfinir votre structure comme ceci :

typedef struct myStruct{
    int a;
    char c[30];
    int f;
} OBJECT;

Sinon, vous ne faites que stocker un pointeur vers une mémoire allouée dynamiquement qui sera détruite à la sortie du programme (ainsi, lorsque vous récupérerez ce pointeur plus tard, l'adresse n'aura aucune valeur et son accès sera très probablement illégal).

1voto

Stu Mackellar Points 8605

Vous sauvegardez un pointeur sur un caractère, pas la chaîne elle-même. Lorsque vous essayez de recharger le fichier, vous vous exécutez dans un nouveau processus avec un espace d'adressage différent et ce pointeur n'est plus valide. Vous devez enregistrer la chaîne de caractères par valeur à la place.

1voto

Sparky Points 4660

Je voudrais ajouter une note sur un problème potentiel de portabilité, qui peut ou non exister en fonction de l'utilisation prévue du fichier de données.

Si le fichier de données doit être partagé entre des ordinateurs de type endian différent, vous devrez configurer les convertisseurs fichier-hôte et hôte-fichier pour les types autres que les caractères (int, short, long, long long, ...). En outre, il pourrait être prudent d'utiliser les types de stdint.h (int16_t, int32_t, ...) à la place pour garantir la taille que vous souhaitez.

Toutefois, si le fichier de données ne se déplace pas, ignorez ces deux points.

1voto

Thomas Matthews Points 19838

En char * de votre structure est connu sous le nom de champ à longueur variable . Lorsque vous écrirez ce champ, vous aurez besoin d'une méthode pour déterminer la longueur du texte. Deux méthodes populaires sont :
1. La taille de l'écriture d'abord
2. Écriture du caractère du terminal

La taille de l'écriture d'abord
Dans cette méthode, la taille des données du texte est écrite en premier, suivie immédiatement par les données.
Avantages : Le texte peut se charger plus rapidement grâce aux lectures en bloc.
Inconvénients : Deux lectures nécessaires, espace supplémentaire requis pour les données de longueur.
Exemple de fragment de code :

struct My_Struct
{
   char * text_field;
};

void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
  size_t text_length = strlen(p_struct->text_field);
  fprintf(output, "%d\n", text_length);
  fprintf(output, "%s", p_struct->text_field);
  return;
}

void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
  size_t text_length = 0;
  char * p_text = NULL;
  fscanf(input, "%d", &text_length);
  p_text = (char *) malloc(text_length + sizeof('\0'));
  if (p_text)
  {
     fread(p_text, 1, text_length, input);
     p_text[text_length] = '\0';
  }
}

Écriture du caractère du terminal Dans cette méthode, les données textuelles sont écrites suivies d'un caractère "terminal". Très similaire à une chaîne de caractères en langage C. Avantages : Requiert moins d'espace que la méthode Size First.
Inconvénients : Le texte doit être lu un octet à la fois pour ne pas manquer le caractère terminal.

Champ de taille fixe
Au lieu d'utiliser un char* en tant que membre, utilisez un char [N] où N est la taille maximale du champ. Avantages : Les enregistrements de taille fixe peuvent être lus comme des blocs. Facilite l'accès aléatoire dans les fichiers. Inconvénients : Gaspillage d'espace si tout l'espace du champ n'est pas utilisé. Problèmes lorsque la taille du champ est trop petite.

Lorsque vous écrivez des structures de données dans un fichier, vous devez envisager l'utilisation d'une base de données . Il en existe de petits comme SQLite et de plus gros comme MySQL. Ne perdez pas de temps à écrire et le débogage des routines de stockage permanent de vos données lorsqu'elles ont déjà été écrites et testé .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X