10
votes

Enregistrement d'une structure C avec une chaîne Char * dans un fichier

J'essaie d'enregistrer une structure avec une chaîne Char * dans un fichier.

#include <stdio.h>
#include <string.h>
#include <fcntl.h>

struct d_object {
    int flags;
    int time;
    int offset;
    char filename[255];
};

int main(int argc, char **argv) {

    struct d_object fcb;

    fcb.flags=5;
    fcb.time=100000;
    fcb.offset=220;
    strncpy(fcb.filename,"myfile",255);


    int fd=open("testfile",O_RDWR);
    write(fd,&fcb,sizeof(fcb));
    close(fd);


    int fd2 = open("testfile",O_RDONLY);
    struct d_object new_fcb; 
    read(fd2,&new_fcb,sizeof(new_fcb));

    printf("read from file testfile: %s\n",new_fcb.filename);

    return 0;

}


2 commentaires

La façon dont vous utilisez Fread est très fausse. Vous ne déclarez pas de tampon de char et que vous y lisez, puis pointez sur un pointeur de structure. Déclarez simplement une structure et transmettez le pointeur à cette structure en fread. La façon dont vous avez fait n'est pas valide C et est susceptible de se bloquer sur des architectures de RISC avec des exigences d'alignement strictes.


Ok, merci pour ça. Cependant, ce n'est pas le problème principal pour le moment.


5 Réponses :


3
votes

La sérialisation n'est jamais jolie. Que diriez-vous de stocker la longueur de la chaîne dans le pointeur et de laisser la chaîne suivre la structure dans le fichier? Quelque chose comme ça (avertissement, code compilé du cerveau): xxx


2 commentaires

Je ne suis pas entièrement d'accord avec la longueur du nom dans un char * . C'est une solution pragmatique et travaille, mais est confuse et considérée comme "style mauvais". Il pourrait même faire exploser si int se révèle plus grand que char * .


@Carl, autant que je suis d'accord en principe, tandis que int pourrait en théorie être plus grand que char * , la valeur de retour de SHLEN ne pourrait jamais être plus grand que char * par un simple argument de comptage. :-) Cela dit, plutôt que de casting, j'utiliserais (char *) 0 + strall (S- puis soustrayez un pointeur null sur le chargement.



1
votes

Nope, il n'y a pas de magie construite directement dans la langue pour le faire pour vous.

Ce que vous devez faire est d'écrire quelques fonctions pour convertir vos données en forme d'écriture et pour la lire dans le fichier. Ceci s'appelle "sérialisation" / "désériorialisation" dans des langues qui font plus de bruit à ce sujet.

Votre structure particulière, vous pouvez faire quelque chose comme écrire les trucs binaires au fichier directement à partir de la structure, puis le suivez avec le contenu du tampon de caractères. Vous pouvez vous faciliter la lecture de choses si vous précéderez les données de caractères avec un int spécifiant sa longueur.

Lorsque vous avez lu ces objets, vous voudrez MALLOC / CALLOC Un morceau de mémoire pour contenir les données Char; Si vous avez stocké la taille, vous saurez à quel point de faire de ce malloc. Lisez les données binaires dans la structure, lisez les données de caractères de caractères dans la mémoire Malloc'd, stockez le pointeur sur le morceau Malloc dans la structure et que vous avez ré-créé l'original.

pas de magie. Juste des données et un peu de graisse au coude.

Modifier

Pendant que j'ai écrit de le faire, Thomas a codé un exemple. Je pense que nos réponses se complètent très bien; ensemble, ils devraient vous dire tout ce que vous devez savoir.


0 commentaires

2
votes

Votre problème ici est vraiment un symptôme d'un problème beaucoup plus vaste: vous ne devriez pas lire / écrire des structures de données binaires entre la mémoire et les fichiers. Non seulement est-il pas de façon claire à lire / structures d'écriture avec des pointeurs vers d'autres données (cela vaut non seulement pour les chaînes, mais les données imbriquées, listes chaînées, etc.), mais le format des données sur le disque dépend de votre machine hôte et C mise en œuvre et ne sera pas portable à d'autres environnements.

Au lieu de cela, vous devez concevoir un format de vos données sur les fonctions de disque et d'écriture pour enregistrer et charger les données ou utiliser un format existant et trouver le code bibliothèque pour l'utiliser. Cela est généralement appelé « sérialisation ».

Si vos données est hiérarchique, JSON, XML ou EBML peut être approprié. S'il est assez simple, les fichiers texte plat ou un homebrew format binaire (écrit octet par octet il est donc portable) peut être approprié.

Puisque vous semblez être peu familiers avec ces questions, il pourrait être intéressant d'écrire un code pour le chargement / sauvegarde quelques formats de fichiers binaires simples (comme .tga ou .wav) comme un exercice avant d'essayer de concevoir quelque chose pour votre propres données.


2 commentaires

Je sais que ce ne sera pas portable et ça va bien car il n'a pas besoin d'être. Ce code est en cours d'exécution sur un système d'exploitation intégré (contiki) avec 10k de RAM! Comme vous pouvez imaginer aucune des technologies que vous avez mentionnées ne fonctionnera à distance, sans parler de ce minuscule système d'exploitation. J'aurais dû faire cela clair dans la question. Merci pour les suggestions de toute façon. J'essaie toujours de voir comment sérialiser les données


Un fichier texte pourrait être la meilleure solution que je pense. Ou si la portabilité n'est pas un problème, pensez-vous qu'il est correct d'écrire un binaire droit dans le fichier?



8
votes

Je comprends que la portabilité n'est pas un problème, puisque vous travaillez pour un système intégré. Dans d'autres cas, vous devez utiliser quelque chose comme XML.

Vous pouvez transformer votre code sur: xxx

puis enregistrer chaque morceau de données individuellement: xxx

pour la lecture, vous devrez lire chaque élément séparément, puis le nom de fichier: xxx


0 commentaires

3
votes

Maintenant que je connais la nature de votre problème, pourquoi ne pas essayer des tableaux flexibles? Au lieu d'utiliser Char * Nom de fichier; , utilisez Nom de fichier [1] et MALLOC (Tailleof struct D_Object + FileName_Len) Pour allouer vos structures. Ajouter un membre de taille et vous pouvez facilement écrire l'objet sur le disque avec un seul appel sur écrire et le charger à partir de disque avec 2 appels (premier pour lire l'élément de taille, seconde pour lire l'objet entier après allouer).

Notez que le moyen «officiel» de faire des tableaux flexibles dans C99 est [] plutôt que [1] , mais [1] est garanti de travailler comme une conséquence d'autres exigences de la norme et fonctionne également sur C89. [1] va gaspillera quelques octets cependant, donc si votre compilateur prend en charge [] , vous voudrez peut-être l'utiliser.


0 commentaires