2010-10-02 20 views
2

J'ai une fonction C qui écrit des données dans un fichier texte. Les données sont constituées de flottants, d'entiers et de chaînes. il ressemble à ceci:une seule fonction C pour écrire des données au format texte ou binaire

writeAsTextFile( mystruct_t* myStructWithIntsFloatsAndStrings , const char* fileName); 

pour ce faire j'utilise des appels à fprintf;

Maintenant je voudrais écrire les mêmes données mais comme binaire. Je pourrais écrire une deuxième fonction writeAsBinaryFile et utiliser des appels à fwrite à la place. Mais à chaque fois que je vais changer la conception de mystruct_t, je devrai modifier à la fois writeAsTextFile et writeAsBinaryFile. Et bien sûr le readAsTextFile correspondant et readAsBinaryFile. En plus de cela, cela va augmenter la taille des codes. Je voudrais donc avoir une seule fonction générique avec un argument bin ou texte qui ressemblerait à ceci:

writeToFile(mystruct_t* myStructWithIntsFloatsAndStrings , const char* fileName, myEnumType_t eOption) 

où l'option serait un ENUM Ebin = 0 et eTxt = 1 par exemple. En fonction de eOption, la fonction écrira des données binaires ou textuelles.

Je ne sais pas quelle serait la meilleure façon d'y parvenir. Dois-je utiliser fwrite aussi pour écrire en tant que texte, Devrais-je essayer d'utiliser des macros? (J'ai vu l'utilisation de la directive ## quelque part mais je ne l'ai jamais utilisée), ou des instructions switch/ifs partout où j'ai besoin d'écrire dans un fichier? Ou dois-je écrire une fonction générique comme myWriteFunction(void *data, char const type, myEnumType_t eOption)

qui serait appelée par writeToFile?

Je ne suis pas très familier avec l'utilisation fread/fwrite et macros de sorte que tout meilleurs commentaires pratiques, idées, etc sont les bienvenus,

Merci

Baba

+0

Alors que vous êtes dans le mode de conception d'un fichier de données, avez-vous considéré quelque chose de plus universel comme JSON ou XML? Alors toutes les écritures sont des écritures de texte. Vous serez capable de lire votre fichier à partir de presque n'importe quel autre environnement de programmation et ce n'est pas plus de travail que ce que vous faites maintenant. Tenez-vous sur les épaules des géants qui marchent devant vous. – dawg

Répondre

1

Vous pouvez créer deux fonctions pour écrire différents types de données dans votre struct:

writeInt(File *f, myEnumType_t eOption, int data); 
writeFloat(File *f, myEnumType_t eOption, float data); 
writeFloatArray(File *f, myEnumType_t eOption, float *data, size_t n_data); 

.. le test binaire ou texte est caché dans chacun de ceux-ci. Votre principale fonction d'écriture struct ressemblerait (avec vérification d'erreur omis):

writeToFile(mystruct_t *myStruct, const char *fileName, myEnumType_t eOption) 
{ 
    const char *fmode = eOption == EOPT_BIN ? "wb" : "w"; 
    FILE *f = fopen(filename, fmode); 

    writeInt(f, eOption, myStruct->a); 
    writeInt(f, eOption, myStruct->b); 
    writeFloatArray(f, eOption, myStruct->values, myStruct->n_values); 
    /* ... */ 
} 

donc un changement à la disposition de la structure n'a qu'à changer un seul endroit.

Vous pouvez également implémenter différentes fonctions d'écriture pour différents "types" d'application. Par exemple, writeTemperature() peut être distinct d'un writeFloat() générique.

1

Pour votre situation, il suffit de faire une fonction d'emballage :

writeToFile(...,bool isBinary) { 
    if (isBinary) { 
    // write as binary file 
    } else { 
    // write as text file 
    } 
} 

en ce qui MACROS vont, ils ne sont utiles que si vous voulez que toutes les opérations soient de binaire ou texte:

#ifdef __BINARY 
#define WriteToFile(a,b,c,d,e) WriteToBinary(a,b,c,d,e) 
#else 
#define WriteToFile(a,b,c,d,e) WriteToText(a,b,c,d,e) 
#endif 

Ceci est utilisé dans le winAPI pour basculer entre les fonctions ascii et les fonctions de caractères larges. BTW: Si votre structure contient char * ou std :: string, le contenu de la chaîne ne sera pas copié, mais seulement son adresse. Ceci s'applique à tous les autres pointeurs, tels que int * ou std :: vector.

0

Tout d'abord, ne pas combiner les fonctions, il n'y a pas d'économie pratique. Deuxièmement, vous devrez écrire une nouvelle fonction d'écriture en tant que texte chaque fois que vous utiliserez la structure, il n'y a aucun moyen de contourner cela (à l'exception de certaines bibliothèques de sérialisation non standard). Troisièmement, tant que vous ne changez jamais l'ordre des membres de la structure et ajoutez uniquement de nouveaux membres à la fin de la structure, vous n'aurez jamais besoin d'écrire un nouvel éditeur ou lecteur binaire. Pour ce faire, vous devez écrire la taille de la structure dans le fichier, puis écrire la structure dans le fichier. De cette façon, votre fonction de lecture lira la taille de la structure (quand elle a été écrite) et saura combien d'octets lire.Si votre structure change, votre programme pourra alors lire les parties de l'ancienne version de struct, et les nouveaux membres à la fin de la structure ne seront pas initialisés.

EDIT

L'écriture d'un struct avec des pointeurs rédigera la valeur du pointeur. Vous devez être très prudent lorsque vous lisez la structure, car le pointeur indiquera une paix de mémoire essentiellement aléatoire.b

Si vous voulez maintenir les relations entre les structures, vous devrez créer votre propre mécanisme. Cela varie en difficulté, vous devrez trouver un ordre prédéfini pour l'écriture des structures, et la reconstruction de tous les pointeurs lorsque vous lisez la structure.

+0

Merci. J'aurais dû mentionner que ma structure contient des pointeurs vers des tableaux de flottants, et divers autres structs contenant des tableaux de flottants, ints etc. Je pourrais garder une trace de la taille totale de mystruct mais quand même, quand j'appelle fwrite pour écrire la structure entière suppose que seuls les pointeurs vers mes tableaux alloués seront copie d et non le contenu du tableau. Ai-je raison? – Baba

+0

@Baba, mis à jour ma réponse pour répondre à cela. – mikerobi