Enregistrer un tableau dans un fichier

cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005 - 21 avril 2005 à 15:43
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005 - 24 avril 2005 à 21:05
Bonjour !

Je développe une application en mode console en C++.
Voici le thème : je veux gérer des clients qui réalisent plusieurs abonnements à un magazine.

J'ai donc créé une classe C_client et une classe C_abonnement. Je sauvegarde mes données dans un fichier lorsque l'utilisateur saisit le sous menu 5 pour sortir de l'application.

Or, la sauvegarde d'un client sans abonnements ne pose aucun problème. Seulement lorsque je lui ajoute un abonnement (chaque client possède un tableau d'abonnements), l'enregistrement des données quant à cet abonnement se trouvent erronées.

Voici ma procédure d'enregistrement de fichiers :

#include <string>
#include
#include <fstream>

void enregFichier()
{
int i;

// si le fichier est lisible...
if ( is_readable( "abonnements.txt" ) )
{ // ...alors on écrit dedans en effacant tout
std::ofstream file( "abonnements.txt" );
for (i=0;i<MAXIMUM;i++)
{

fwrite(&tabClient[i], sizeof(tabClient[i]), 1, monFichier);
};
};

Un extrait de ma classe C_client :

class C_client
{


// Données privées
private:
int num_client;
char nom[20];
char prenom[20];
char adresse[50];
char cp[5];
char ville[20];
char telephone[10];
int nb_abonnements; // nombre total d'abonnements
C_abonnement *tabAbonnements[MAX]; // tableau d'abonnements
};

Et un extrait de ma classe C_abonnement :

class C_abonnement
{
//Données privées
protected:
int numAb;
public:
int getNumAb()
{
return numAb;
};

void getLibelle()
{
cout << libelle;
};
};

Je me demande pourquoi le numéro de client ressort erroné (il vaut -842150451 au lieu de 25 par exemple).


Merci d'avance pour votre aide.

20 réponses

luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
21 avril 2005 à 17:42
C'est quoi tabClient, dans le sens, tu le construit comment, ca contient quoi ?

car je crains que l'erreur vienne de la.

Poste les lignes de codes concernant tabClient.



J'ai l'impression que tu te mélanges un peu pour écrire fans les fichiers.

Ce qui m'étonne c'est que tu utilises deux librairies différentes en meme temps:

std::ofstream file( "abonnements.txt" );
for (i=0;i<MAXIMUM;i++)
{

fwrite(&tabClient[i], sizeof(tabClient[i]), 1, monFichier);
};



Ce serait pas plutot:




std::ofstream file("abonnements.txt");

for (int i=0; i<MAXIMUM; i++)

file << tabClient[i] << endl;



tu utilises fwrite qui vient le stdio.h il me semble. En tout cas, il vaut mieux utiliser la syntaxe: file <<

bien adapter au format texte.
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
21 avril 2005 à 18:20
Voici la déclaration de tabClient, il contient tous mes clients de la classe C_client

const int MAXIMUM = 100;
C_client tabClient[MAXIMUM];

Avant j'utilisais la fonction suivante pour enregistrer dans un fichier :

monFichier = fopen("abonnements.txt", "w"); // ouverture du fichier en écriture


if (monFichier == NULL)
{
cout << "Erreur lors de l'ouverture du fichier Abonnements." << endl;
}
else
{
int i;


for (i=0;i<MAXIMUM;i++)
{
tmp = fwrite(&tabClient[i], sizeof(tabClient[i]), 1, monFichier); // on enregistre ligne par ligne dans le fichier


if (!tmp)
{
cout << "Erreur d'ecriture." << endl;
};
//else
//{
// cout << tabClient[i].getNumClient() << endl;
//};
};
fclose(monFichier);
};

Cependant, dès que j'enregistrais un abonnement pour un client, mon fichier n'enregistrait aucune données.

La façon de mon premier post peut elle enregistrer des données de type C_client (même s'il y a des integer, etc dans la classe) ?
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
21 avril 2005 à 20:46
Bon je commence a comprendre, mais comment veux tu que ca marche ca:

std::ofstream file( "abonnements.txt" );
for (i=0;i<MAXIMUM;i++)
{

fwrite(&tabClient[i], sizeof(tabClient[i]), 1, monFichier);
};




&tabClient[i],
ca c'est l'adresse en mémoire de ton client, mais pas l'objet lui meme.
DOnc au mieux ce que tu écris c'est l'adresse, qu'on se fou absolument.



Donc j'utilise ma version qui est plus lisible: ton code donnerait:






std::ofstream file("abonnements.txt");

for (int i=0; i<MAXIMUM; i++)

file << "N°: " tablClient[i].num_client << " |
Nom : " << tabClient[i].nom << ...etc... << endl;



Il y a plus élaboré et plus lisible:

Tu ajoutes cette fonction en dessous de ta classe (c'est pas une fonction membre)



inline std::ostream & operator << (std::ostream & chaine, const C_client & client)

{

return chaine <<
"N°: " client.num_client << " | Nom : " << client.nom; // tu ajoutes ce que tu veux après


}



Puis après l'écriture dans ton fichier devient simplement:






std::ofstream file("abonnements.txt");

for (int i=0; i<MAXIMUM; i++)

file << tabClient[i] << endl;



file.close();



++
0
steve_clamage Messages postés 475 Date d'inscription dimanche 3 octobre 2004 Statut Membre Dernière intervention 11 août 2006 5
21 avril 2005 à 21:12
Le fwrite enregistre l'objet en binaire et non son adresse, en C++ on utiliserais plutot std::ostream::write.



cyberkate, pour faire propre il faudrait que ce soit tes classes qui
fournissent une methode de serialisation (= sauvegarde d'une instance
dans un fichier) car la façon d'enregistré une instance dépends des
attributs (si pointeur sur tableau dynamique par exemple).



class C_client
{



public:



void enregistre( std::ostream & o ) const

{

o.write( reinterpret_cast<const char *>(&num_client), sizeof num_client );

// comme ca pour tout les membres

}



};




std::ofstream file("abonnements.txt", std::ios::binary);
for (int i=0; i<MAXIMUM; i++)


tabClient[i].enregistre(file);
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
22 avril 2005 à 11:36
Pour la méthode de steve_clamage, j'ai le problème suivant : tous mes champs qui ne sont pas de type chaine ne peuvent être convertis en chaine... Voici ce que j'ai mis comme code :

void enregistre( std::ostream & o ) const
{
o.write( reinterpret_cast<const int>(&num_client), sizeof num_client );
o.write( reinterpret_cast<const char *>(&nom), sizeof nom );
o.write( reinterpret_cast<const char *>(&prenom), sizeof prenom );
o.write( reinterpret_cast<const char *>(&adresse), sizeof adresse );
o.write( reinterpret_cast<const char *>(&cp), sizeof cp );
o.write( reinterpret_cast<const char *>(&ville), sizeof ville );
o.write( reinterpret_cast<const char *>(&telephone), sizeof telephone );
o.write( reinterpret_cast<const int>(&nb_abonnements), sizeof nb_abonnements );
o.write( reinterpret_cast<const C_abonnement *>(&tabAbonnements), sizeof tabAbonnements );
};

Quant à la méthode de luhtor, j'ai essayé avec ce code dans la procédure enregFichier :

int i;


// si le fichier est lisible...
if ( is_readable( "abonnements.txt" ) )
{
std::ofstream file("abonnements.txt");
for (int i=0; i<MAXIMUM; i++)
{
file << tabClient[i].getNumClient() << tabClient[i].getNom() << tabClient[i].getPrenom() << tabClient[i].getAdresse() << tabClient[i].getCp() << tabClient[i].getVille() << tabClient[i].getTelephone() << tabClient[i].afficheLesAbonnements() << endl;
};
};

J'ai l'erreur suivant à la ligne débutant par "file" : error C2679: binary '<<' : no operator defined which takes a right-hand operand of type 'void' (or there is no acceptable conversion)

Ne peut-on pas enregistrer dans le fichier directement un objet ? J'aimerais conserver le type de chaque élément.

Je précise que pour récupérer un donnée de type chaine, je ne sais pas trop comment faire :
void getPrenom()
{
cout << prenom;
};
ou

char *getTelephone()
{
return telephone;
};
??

De plus, pour inscrire dans le fichier l'ensemble du tableau tabAbonnements de chaque client, je ne sais pas comment m'y prendre. Faut-il refaire une autre boucle pour inscrire chaque donnée de chaque objet du tableau ?
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
22 avril 2005 à 12:27
tabClient[i].getPrenom()



avec ca:



void getPrenom()
{
cout << prenom;
};


oui en effet, ca ne peut pas marcher puisque ta fonction ne retourne rien.

Ta pas essayer ma deuxième solution qui ne devrait pas poser de
problème, mais il faudrait la déclarer friend car tu as des données
private.



Bref, ta fonciton getPrenom

J'utiliserais des string, ca donnerait:



std::string getPrenom()

{

return std::string(this->prenom);

}



Mais selon moi tu aurais du utiliser des string et non des char * pour tes données de ta classe.
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
22 avril 2005 à 14:14
Je pense que cela doit se voir que je débute vraiment en C++ ! lol

J'arrive pas du tout à faire ce que tu me dis alors que je fais du copier coller en essayant d'adapter le code. J'obtiens une bonne dizaine d'erreurs...

J'ai du mal à comprendre comment faire pour enregistrer donnée par donnée dans mon fichier afin de les relire lors du prochain lancement de l'application.

J'ai mis mes fichiers en ligne si ça vous aide à comprendre mon problème : ils ne contienent aucune modification par rapport à mon post initial.
Fichiers .cpp et .h

Je continue à chercher en attendant votre aide, car je suis vraiment perdue et que je dois présenter ce projet en examen...

Merci d'avance pour vos autres explications.
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
22 avril 2005 à 20:27
J'ai rajouté ce code dans le public de ma classe C_client (si je le mets en dessous, il y a une erreur demandant de mettre la fonction friend à l'intérieur d'une classe) :

friend inline std::ostream & operator << (std::ostream & chaine, const C_client & client)
{
return chaine << client.num_client << " " << client.nom << " " << client.prenom << " " << client.adresse << " " << client.cp << " " << client.ville << " " << client.telephone;
};

Ensuite, voici le contenu de ma fonction enregFichier :

if ( is_readable( "abonnements.txt" ) )
{ // ...alors on écrit dedans en effacant tout
std::ofstream file("abonnements.txt");
for (int i=0; i<MAXIMUM; i++)
{
file << tabClient[i] << endl;
};
file.close();
};

Voici le nouveau problème : il n'y a pas "d'erreur" lors de l'écriture mais lorsque je réouvre l'application, les données sont affichées avec plein de fois 0040119F0 entre et après....

Voici le code qui charge les données dans mon tableau à l'ouverture de l'application :
void chargeClient()
{
monFichier = fopen("abonnements.txt", "r+"); //ouverture du fichier en lecture


if (monFichier == NULL)
{
cout << "Erreur d'ouverture du fichier Abonnements." << endl;
}
else
{
int i;
for (i=0;i<MAXIMUM;i++)
{
tmp = fread(&clientEnCours, sizeof(clientEnCours), 1, monFichier); // on lit une ligne par une ligne


if (!tmp) // si la ligne est vide
{
cout << "Erreur de lecture." << endl;
}
else
{
tabClient[i] = clientEnCours;
};
};


fclose(monFichier); // fermeture du fichier
};
};

D'où vient le problème ?...
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
22 avril 2005 à 20:37
Cette fonction la marche très bien, mais ca doit etre une erreur simple. Donne les erreurs que tu obtiens.

std::string getPrenom()

{

return std::string(this->prenom);

}



Et précises comment tu l'utilises ensuite.

C'est une obligation de ton projet d'utiliser des char * ou c'est toi qui choisit ?
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
22 avril 2005 à 20:51
Pour ce qui est des char *, je ne pense pas que ce soit une obligation, mais c'est qu'on a appris que comme ça. Donc j'ai repris le "cours" et ce que j'avais à peu près compris.

En laissant les fonctions de mon dernier post et en changeant les char * en std::string, je n'ai pas d'erreurs dans le débuggueur, mais les données dans le fichier ne sont pas correctes.

J'utilise cette fonction comme ceci : tabClient[i].getNom(); pour afficher le nom...

J'ai mes données et plein de 0040119F0 0040119F0 0040119F0...

Donc je ne comprends pas... peut-être que je les lis mal...
Merci de ton aide.
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
22 avril 2005 à 22:52
:)

Poste ta fonction qui enregistre dans un fichier, car ce que tu as ce
sont des adresses mémoires donc quelque part tu écris un pointeur et
nom un string.
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
23 avril 2005 à 11:10
Voici ma fonction qui enregistre dans un fichier :

void enregFichier()
{
int i;


// si le fichier est lisible...
if ( is_readable( "abonnements.txt" ) )
{ // ...alors on écrit dedans en effacant tout
std::ofstream file("abonnements.txt");
for (int i=0; i<MAXIMUM; i++)
{
file << tabClient[i] << endl;
};
file.close();
};
};

Ce qui est bizarre, c'est que ça enregistre mes données ainsi que les adresses mémoires.

Il est à noter que :

- je ne sais pas comment intégrer mon tableau d'abonnements dans la fonction friend :
friend inline std::ostream & operator << (std::ostream & chaine, const C_client & client)
{
return chaine << client.num_client << " " << client.nom << " " << client.prenom << " " << client.adresse << " " << client.cp << " " << client.ville << " " << client.telephone; // comment intégrer le tableau d'abonnements ?
};

- je n'arrive pas à lire les données (nom, prenom, adresse, etc) avec ma procédure afficheInfo avec dedans :
tabClient[i].getNom();
etc.
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
23 avril 2005 à 12:22
Tu fais bien cout << tabClien[i].getNom() << endl;



avec getNom() qui retourne un string ?


Pour tes abonnements:


friend inline std::ostream & operator << (std::ostream & chaine, const C_client & client)
{
chaine << client.num_client << " " << client.nom
<< " " << client.prenom << " " <<
client.adresse << " " << client.cp << " " <<
client.ville << " " << client.telephone;



for (int i = 0; i<nombre d'abonnements; i++)

{

chaine << client.abonnements[i].nom de l'abonnement << " " << d'autre truc << endl;

}

return chaine;
};
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
23 avril 2005 à 13:26
Si je fais cout << tabClient[i].getNom() << endl; dans ma fonction afficheInfo(), j'ai l'erreur suivante de compilation :

error C2678: binary '<<' : no operator defined which takes a left-hand operand of type 'class ostream_withassign' (or there is
no acceptable conversion)

Et getNom() retourne un string :
std::string getNom()
{
return std::string(this->nom);
};

Et si je mets la fonction :
friend inline std::ostream & operator << (std::ostream & chaine, const C_client & client)
{
chaine << client.num_client << " " << client.nom << " " << client.prenom << " " << client.adresse << " " << client.cp << " " << client.ville << " " << client.telephone; // tu ajoutes ce que tu veux après

for (int i = 0; i< client.nb_abonnements; i++)
{

chaine << client.tabAbonnements[i].getNumAb() << " " << client.tabAbonnements[i].getLibelle() << endl;
};
};

celà me fait 2 erreurs de même type :
- error C2228: left of '.getNumAb' must have class/struct/union type
- error C2228: left of '.getLibelle' must have class/struct/union type

Je ne peux "accéder" aux données privées relatives aux abonnements que par l'intermédiaire de fonctions :

int getNumAb()
{
return numAb;
};

std::string getLibelle()
{
return std::string(this->libelle);
};
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
23 avril 2005 à 15:06
Ca devrait marcher. Ta bien mis

#include

#include <string>

#include <fstream>



using namespace std;



en entete ?
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
23 avril 2005 à 15:40
J'ai bien mis les bibliothèques iostream, string, ftream en en-tête.

Par contre, pour ce qui est du using namespace std; , si je le mets en en-tête, j'ai 59 erreurs car, apparemment, le cout et le cin ne sont pas acceptés :

error C2872: 'cout' : ambiguous symbol
error C2872: 'cin' : ambiguous symbol

Comment faire pour mettre le using namespace std; sans avoir les erreurs concernant le cout et le cin

PS : voici un lien avec les fichiers .cpp et .h, au cas où.
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
23 avril 2005 à 16:18
Le prend pas mal, mais j'ai halluciné :)

Faut surtout pas mettre iostream et iostream.h ainsi que string et string.h.



donc en entete de main tu mest ca:



#include <stdio.h>

#include <stdlib.h>

#include <string>

#include

#include <fstream>

using namespace std;



#include "classes.h"

et ca marche impec.

enfin j'ai vu un bug quand j'ai essayer de créer un abonnement



De plus dans classe.h,

for (int i = 0; i< client.nb_abonnements; i++)

{




chaine <<
client.tabAbonnements[i]->getNumAb() << " " <<
client.tabAbonnements[i]->getLibelle() << endl;


//chaine << client.abonnements[i].num_ab
<< " " << d'autre truc << endl;

};



Ce sont des flèches qu'il faut mettre, puisque ce sont des pointeurs
qui accèdent aux données, donc regarde ou j'ai mis les flèches juste
avant.
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
24 avril 2005 à 11:30
Oh je ne le prends pas mal. Cela me semblait bizarre de mettre autant de bibliothèques avec les mêmes noms, mais bon... lol

J'ai corrigé ce que tu m'as dit.
Je ne savais pas que c'était des pointeurs qui accédaient aux données (d'ailleurs, on le voit comment ??)

Sinon j'ai testé l'application : l'inscription dans le fichier se fait bien... par contre je n'arrive pas à relire les informations...

Y-a-t-il quelquechose qui ne va pas dans ma fonction chargeClient ??
Je ne sais pas si je remplis bien mon tableau tabClient et si je le lis bien...

Peux tu m'aider sur ce point ??
Merci beaucoup

Je te re-poste ma fonction chargeClient() au cas où...

monFichier = fopen("abonnements.txt", "r+"); //ouverture du fichier en lecture


if (monFichier == NULL)
{
cout << "Erreur d'ouverture du fichier Abonnements." << endl;
}
else
{
int i;
for (i=0;i<MAXIMUM;i++)
{
tmp = fread(&clientEnCours, sizeof(clientEnCours), 1, monFichier); // on lit une ligne par une ligne


if (!tmp) // si la ligne est vide
{
cout << "Erreur de lecture." << endl;
}
else
{
tabClient[i] = clientEnCours;
};
};


fclose(monFichier); // fermeture du fichier
};
0
luhtor Messages postés 2023 Date d'inscription mardi 24 septembre 2002 Statut Membre Dernière intervention 28 juillet 2008 6
24 avril 2005 à 19:00
Est ce que tu peux reposter :) car la ca devient pénible d'accéder à ce post ^^



De mémoire pour ta question: c'est des pointeurs on le voit comment ?

Et bien c'est toi qui a définit ca comme ca:

un truc du genre:

Abonnements *abonnement[MAX] <===== ca c'est un tableau de pointeur.
Donc il contient que des pointeurs et quand tu y fais référence, c'est
un pointeur.
0
cyberkate Messages postés 13 Date d'inscription jeudi 21 avril 2005 Statut Membre Dernière intervention 25 avril 2005
24 avril 2005 à 21:05
Merci pour ton aide !
Je crée un autre post pour le problème de lecture des données.

@+
0
Rejoignez-nous