Pages: 1
- Accueil forums
- » Développement en C++
- » lire et ecrire un fichier de sauvegarde très simplement
08-09-2010 22:52:04
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 79.92.239.57
- Courriel
[intro] Récupérer une information enregistré dans un fichier où d'enregistrer une information dans un fichier.
Il écrit les informations désirées de façon a rendre sa lecture/modification manuel particulièrement facile.
La manipulation de fichier pour un système de sauvegarde par exemple peut s'avérer parfois pénible. Bien souvent il ressemble plutôt à çà:
le truc un peut barbant, il faut etre super organiser pour bien lire les données dans l'ordre, et bien les réécrire. Puis quand on veut y faire une modif manuellement, on ne sais plus vraiment à quoi ça correspond tout çà ![]()
FINI l'ennuie, voici un petit bout de code qui va bien changer tout çà, maintenant on lit/enregistre ce qu'on veut, quand on veut, dans n'importe quel ordre
grâce à 2 fonctions seulement (en passant j'en profite pour remercier Nico qui m'a mis sur la piste des template).
ancres->set(LE CODE, TEST DE LECTURE, TEST D'ECRITURE, POUR FINIR, PETITE ASTUCE); [/intro]
ancres->create(LE CODE);
voici tout le code à mettre dans un fichier .h:
Code c++ :
#ifndef NA_FINDORWRITEINFILE_H
#define NA_FINDORWRITEINFILE_H
/*
NA_FindOrWriteInFile
créer le 11/09/2010
Role: recupérer une information enregistré dans un fichier
où enregistrer une information dans un fichier.
Il ecrit les informations desirées de façon a rendre sa lecture/modification
manuel particulerment facile.
L'extention du fichier peut être de n'importe quelle type (txt, ini, dat... et même aucune)
exemple du fichier:
prenom= Nabouill
age= 27
pays= France
*/
#include <stdio.h>
#include <string.h>
#include <sstream>
/// ***********************************************************************************
/// GET INFO IN FILE
/// ***********************************************************************************
template<typename T>
bool getInfoInFile(const char path[], std::string info, T & Data )
{
info += "=";//rajoute le "=" a l'info pour la recherche dans le fichier
int longueurInfo = info.size();
char ligne[256];//pour lire la ligne
std::string sparam; //pour remettre le parametre lu dans un string
FILE* fichier = fopen(path, "r");//ouverute du fichier
rewind(fichier);//on revient au debut du fichier pour etre sûr
bool continuer = true;
while(continuer)
{
if(fgets(ligne, 255, fichier) != NULL)
{ //on recherche dans cette ligne l'info et on verifie si on la trouve
if(strstr(ligne, info.c_str()))
{ //si on la trouve
int longueurLigne = strlen(ligne);
//on recule dans le fichier jusqu'au =
fseek(fichier, (longueurInfo - longueurLigne -1), SEEK_CUR);
//on recuperer le parametre de l'info dans le fichier
char paramTemp[256];
fscanf(fichier, "%s", paramTemp);
//on remet ça dans un string
sparam = paramTemp;
continuer = false;
}
}
else// sinon c'est qu'on n'a jamais trouvé l'info
{ //on ecrit donc une valeur par default
sparam = "0";
continuer = false;
}
}
//on ferme le fichier
fclose(fichier);
//convertion du string en parametre a renvoyer
std::istringstream param( sparam );
return param >> Data != 0;
}
/// ***********************************************************************************
/// SAVE INFO IN FILE
/// ***********************************************************************************
template<typename T>
void saveInfoInFile(const char path[], std::string info, T & Data )
{
//convertion du Parametre du type envoyer en string
std::ostringstream oss;
oss << Data;
std::string sparam = oss.str();//contient le parametre
info += "=";//rajoute le "=" a l'info pour la recherche dans le fichier
FILE* fichier = fopen(path, "r");//ouverute du fichier
//si on arrive pas, on suggere qu'il n'existe pas, donc on le crée
//et on ecrit l'info + le parametre a sauvegarder dedans
if(fichier == NULL)
{
fichier = fopen(path, "a");//dans on le cree
rewind(fichier);//on revient au debut du fichier pour etre sûr
fprintf(fichier, "%s %s\
",info.c_str(), sparam.c_str());//on ecrit dans le fichier
fclose(fichier);//on ferme le fichier
}
else// on a reussi a ouvrir le fichier
{
//on creer un fichier temporaire
std::string pathTemp = path;
pathTemp+= "Temp";
FILE *fichierTemp = fopen(pathTemp.c_str(), "w+");
char ligne[256];
bool infoExistait = false;
bool continuer = true;
while(continuer)
{
if(fgets(ligne, 255, fichier) != NULL)
{ //on recherche dans cette ligne l'info
if(strstr(ligne, info.c_str()))//si on trouve
{ infoExistait = true;
//on ecrit son nouveau parametre dans le fichier temp
fprintf(fichierTemp, "%s %s\
",info.c_str(), sparam.c_str());
}
else//si on ne la trouve pas
{
//on ecrit dans le fichier temp ce qu'on a trouver dans le fichier original
fprintf(fichierTemp, "%s",ligne);
}
}
else// si on a fini de lire le fichier
{
if(!infoExistait)//si l'info n'hexistait pas, on la rajoute a la fin du fichier
{ fprintf(fichierTemp, "\
%s %s",info.c_str(), sparam.c_str());
}
continuer = false;
}
}
//arrivé ici, le fichier temporaire est complet
fclose(fichier); //fermeture du fichier original
remove(path); //suppression du fichier original
rewind(fichierTemp); //revient au debut du fichier temp
fichier = fopen(path, "w+"); //recreation du fichier original
continuer = true;
while(continuer)
{ //recopie integral du fichier temp dans le nouveau original
if(fgets(ligne, 255, fichierTemp) != NULL)
{ fprintf(fichier, "%s",ligne);
}
else
{ continuer = false;
}
}
//fermeture et suppression du fichier temp
fclose(fichier);
fclose(fichierTemp);
remove(pathTemp.c_str());
}
}
#endif //NA_FINDORWRITEINFILE_HJe ne vais pas m'attarder sur ce qu'il y a dans ce code, je l'ai déjà commenté abusivement.
On va donc passer tout de suite à ce qu'il faut retenir, ce sont juste ces 2 prototypes de fonctions enfaite:
Code c++ :
bool getInfoInFile(const char path[], std::string info, T & Data )
void saveInfoInFile(const char path[], std::string info, T & Data )Ces fonctions prennent en paramètre:
1- le chemin du fichier que l'on veut lire ou écrire
2- l'information recherché ou a écrire dans le fichier
3- la donnée que l'on veut enregistrer
/i\\ Le dernier paramètre des ces fonction peut être de n'importe quel type (int, long, char*, string, float...) /i\\
Et grâce à çà, voilà à quoi ressemble mes fichiers de sauvegarde maintenant:

C'est quand même un peut plus lisible que ce qu'on peut trouvé parfois.
ancres->create( TEST DE LECTURE);
En gros on n'a plus qu'a demander "c'est qui ami1 ?" il me reste combien de vie ?
pour çà, on a juste a demander ce que j'appel l'information (qui se trouve avant le signe "=") et il nous renvoi ce que j'appel le Data, après le "=".
Un petit code d'exemple qui va lire le fichier de sauvegarde situer juste au dessus:
Code c++ :
#include <iostream>
#include "NA_FindOrWriteInFile.h"
using namespace std;
int main()
{
char path[] = "sauvegarde/config.ini";
int vieRestante;
float posi;
char ami_3[32];
string ville_2;
getInfoInFile(path, "nbvie", vieRestante);
getInfoInFile(path, "ville2", ville_2);
getInfoInFile(path, "position", posi);
getInfoInFile(path, "ami3", ami_3);
cout <<"vie Restante: " << vieRestante << endl;
cout << "Ville 2: " << ville_2 << endl;
cout << "posi: " << posi << endl;
cout << "Ami 3: " << ami_3 << endl;
return 0;
}ce que ça nous dis:

BINGO!
ancres->create( TEST D'ECRITURE);
On peut aussi enregistrer ce qu'on veut dans notre fichier, si une information n'existe pas, il l'a rajoute, si elle existe, il remplace sont Data.
exemple:
Code c++ :
#include <iostream>
#include "NA_FindOrWriteInFile.h"
using namespace std;
int main()
{
char path[] = "sauvegarde/config.ini";
int vieRestante = 15;
float posi = 155.789;
char ami[32] = "Jo";
string ville = "Villepot";
saveInfoInFile(path, "nbvie", vieRestante);
saveInfoInFile(path, "position", posi);
saveInfoInFile(path, "ami1", ami);
saveInfoInFile(path, "ville2", ville);
//test a la mano
saveInfoInFile("sauvegarde/config.ini", "nom_du_chien", "Dédé");
return 0;
}
voici maintenant à quoi ressemble notre fichier de sauvegarde:

Vous pouvez comparer; c'est impec
ancres->create( POUR FINIR);
/i\\Info/i\\
On peut très bien rajouté des commentaires à notre fichier, cela ne pose aucun problème.
/!\\ Par contre /!\\ sur une modification manuel, il est important de bien mettre le signe "=" collé à l'information, sans espace.
Mais après le signe "=" peut importe, écrire:
ami1=pierre
ami2= paul
donne exactement le même résultat.
ancres->create( PETITE ASTUCE);
Si cette class vous plait, vous pouvez l'enregistrer ce fichier .h directement avec les fichiers des lib standard de votre compilateur (exemple pour moi avec code::block "C:\\Program Files\\CodeBlocks\\MinGW\\include"), car si on compte utiliser cette class dans plusieurs programmes, on n'a plus qu'à le mettre en entête a notre projet sans être obligé de copier le fichier dans tous nos programmes.
Hors ligne
09-09-2010 01:15:55
- nico
- Administrateurs

- Date d'inscription:
- Messages: 563
- IP: 82.232.128.163
- Courriel
Salut nabouill, merci pour ce nouveau tuto
c'est bien pratique, par contre je trouve qu'il y a trop de répétitions et du coup le code est super long
je pense que ça serait plus simple avec un seul get et un seul save (facile à dire
), quite à créer une petite classe suplémentaire et/ou un template pour gerer les types, qu'en penses-tu ?
Hors ligne
09-09-2010 06:02:58
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 79.92.239.57
- Courriel
oui, je vois bien ce que tu veut dire, mais j'avoue que je vois pas du tout comment gérer les types, car c'est vai qu'au début l'idée était de faire un seul "getInfo()" et un seul "saveInfo()". Mais comment diable ! :mad
N'ayant pas la solution, j'ai donc créer un get() et un save() pour chaque type courant.
Pour me consoler
je me disait que ce n'était pas très important, car si on utilise ma petite astuce que j'ai mis a la fin, on ne voit plus le code, donc qu'il soit long ou pas, ça ne change pas grand chose.
Hors ligne
09-09-2010 20:10:12
- nico
- Administrateurs

- Date d'inscription:
- Messages: 563
- IP: 82.232.128.163
- Courriel
j'ai pas eu le temps de bien approfondir, mais l'idée de départ c'est de faire un template
Code c++ :
template<typename TypeInconnu> TypeInconnu getInfo(std::string info)
{
.......
return sparam;
} puis dans le main:
Code c++ :
int nombreVie = finder.getInfo<int>("nbvie");
std::string maVille_2 = finder.getInfo<std::string>("ville2");voilà, tu as juste à définir le "type" voulu lors de l'appel de la fonction.
Après il faut s'occuper de l'intèrieur de la fonction mais j'ai pas encore eu le temps...
Hors ligne
09-09-2010 20:49:48
- TUpac
- Membres

- Date d'inscription:
- Messages: 387
- IP: 88.168.3.38
- Courriel
ouai j'ai cherché un peut mais je ne vois vraiment pas comment getInfo saurait quelle conversion faire. Je ne trouve pas le moyen dans une fonction template de faire un "switch" entre les types d'entrée.
Parce que sans ça, comment faire la diférence entre int et string ?
Si tu trouve ça me ferait une bonne lecon de prog ![]()
"Si vous ne partagez pas votre stabilité avec les pauvres, les pauvres partageront leur instabilité avec vous."
Hors ligne
09-09-2010 22:20:21
- nico
- Administrateurs

- Date d'inscription:
- Messages: 563
- IP: 82.232.128.163
- Courriel
bein il y a les "spécialisations", mais dans le cas ici présent, cela nous renverrai à notre problème de départ, c'est à dire qu'il faut réécrire chaque spécialisation et le code serait trop long, même si on en fait que 2, il y a trop peu d'éléments variable dans la fonction, je trouve que c'est dommage de répéter autant de lignes de code, à moins de placer le code répètes dans une procédure privée. mais bon ça serait pas très propre.
Je sais pas comment on détecte un type, mais c'est sûre que ça serait l'idéal comme tu dit TUpac
Le plus simple dans un premier temps je pense, c'est de passé le type dans un second paramètre de la fonction
edit, étant donnée qu'on récupère l'info sous forme de chaine, on peut déjà faire un isdigit() pour detecter les chiffres, ça sera déjà ça de gagné.
Hors ligne
10-09-2010 14:30:58
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 82.127.58.127
- Courriel
Bon je crois que je vais jeter un œil sur les templates, une chose que je ne connaissait pas, j'ai trouvé ça http://www.commentcamarche.net/faq/11194-les-templates-en-c
Je pense que devrais pouvoir rouler avec çà, quand penser vous ?
Je vais bosser un peut la dessus pour voir. (et si ça ne marche pas, j'aurais au moins appris quelque chose
)
Hors ligne
10-09-2010 14:35:38
- nico
- Administrateurs

- Date d'inscription:
- Messages: 563
- IP: 82.232.128.163
- Courriel
je te conseil ce tuto http://www.siteduzero.com/tutoriel-3-85890-la-base.html ![]()
Hors ligne
10-09-2010 23:12:50
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 79.92.239.57
- Courriel
houhouuu !! je pense avoir trouvé la solution au probleme de convertion
http://cpp.developpez.com/faq/cpp/?page=strings#STRINGS_convert_to
Les premiers test semble pas mal du tout, je recupere ce qu'il y a d'ecrit dans le fichier texte en string, et je converti, pour l'instant avec int, long, double, float, string et char çà roule. je continu a bosser dessus, je vous redit.
merci pour ces pistes.
EDIT:
Une petite question me vient à l'esprit, selon vous, vaut-il mieux créer un objet lui indiquant le path a l'initialisation puis simplement appelé les méthode par la suite, ce qui donne un peut près:
Code c++ :
FindOrWriteFile finder("save/config.ini");
int i;
finder.getInfo("nbvie", i);
std::string texte;
finder.getInfo("ville1", texte);
finder->drop();Où bien faire une simple fonction, mais ce qui oblige du coup à indiquer le path du fichier a chaque fois, ce qui donnerai:
Code c++ :
char path[] = "save/config.ini";
int i;
getInfo(path, "nbvie", i);
std::string texte;
getInfo("path, ville1", texte);Moi à la base je pensait plutôt a la deuxième, mais je me tate un peu. J'aurais aimé avoir d'autre avis.
Hors ligne
11-09-2010 13:51:37
- nico
- Administrateurs

- Date d'inscription:
- Messages: 563
- IP: 82.232.128.163
- Courriel
Là faut demander à Magun c'est lui l'expert ![]()
Hors ligne
11-09-2010 14:36:24
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 79.92.239.57
- Courriel
bon, je part sur la 2em solution, je trouve ça quand même plus light (bien qu'on remarquera qu'elle est un poil moins rapide, car a chaque getInfo(), on ré-ouvre et referme le fichier, mais je pense utiliser ça principalement dans des menu "loading" de programme, donc je ne trouve pas ce léger inconvénient bien méchant, ce n'est pas comme si on travaillait sur une base de donnée partagé)
EDIT: ça devrait être bon dans la soiré.
Hors ligne
11-09-2010 22:49:13
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 79.92.239.57
- Courriel
Voili voilou !
C'est vrai que c'est devenu vraiment plus light, passer de 500 lignes à 150 (malgrés tous les commentaires) pour faire un truc beaucoup plus simple d'utilisation ![]()
Je dis merci de m'avoir mis sur la piste des template.
Hors ligne
11-09-2010 23:13:42
- nico
- Administrateurs

- Date d'inscription:
- Messages: 563
- IP: 82.232.128.163
- Courriel
cool, d'un coup c'est beaucoup plus agréable à lire ![]()
Hors ligne
06-01-2011 12:57:09
- mmorpglefilm
- Membres

- Date d'inscription:
- Messages: 162
- IP: 78.122.228.229
- Courriel Site web
En regardent votre code j'ai eu une très très bonne idée : comme moi je crée un jeu dans l'espace et que je risque de faire beaucoup de meshs, qui devront tous avoir un niveau de vie, place dans la soute, vitesse, bouclier.......ça fait beaucoup....: c'est là que votre code arrive, j'ai eu l'idée de sauvegarder toutes ces infos dans des fichiers !
Comme ça quand je crée un mesh ça ouvre toutes les infos d'un fichier texte avec les coordonnées en 3D, la vie......etc....
Mais je voudrais savoir si en utilisant ces fichiers il était possible de dire à une variable irr::scene::IAnimatedMash que tel mesh se trouve dans tel dossier avec des fichiers textes ?
Euh.....euh...non désoler j'ai trouver tous seul....mes merci quand même pour se code !!!
Hors ligne
25-02-2011 19:42:05
- mmorpglefilm
- Membres

- Date d'inscription:
- Messages: 162
- IP: 78.122.184.135
- Courriel Site web
Et aux faite, il y a une licence sur ton code ? Il est gratuit ?
Merci de me répondre.
Hors ligne
26-02-2011 23:45:27
- nabouill
- Membres

- Date d'inscription:
- Messages: 242
- IP: 77.195.183.134
- Courriel
Il n'y a bien sûr aucune licence pour ce code. Tu peux l'utiliser à volonté, le redistribuer, le modifier, le reredistribuer et tout et tout.
A+
Hors ligne



