lire et ecrire un fichier de sauvegarde très simplement
Proposé par nabouill

le jeudi 09 septembre 2010 à 00h 52mn 04s

6644 visualisations

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 à çà:

http://i27.servimg.com/u/f27/11/17/17/45/oldcon10.jpg
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 çà hmm

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 smile grâce à 2 fonctions seulement (en passant j'en profite pour remercier Nico qui m'a mis sur la piste des template).

LE CODE
TEST DE LECTURE
TEST D'ECRITURE
POUR FINIR
PETITE ASTUCE









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\n",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\n",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, "\n%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_H


Je 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
INFO
Le dernier paramètre des ces fonction peut être de n'importe quel type (int, long, char*, string, float...)



Et grâce à çà, voilà à quoi ressemble mes fichiers de sauvegarde maintenant:

http://i27.servimg.com/u/f27/11/17/17/45/config11.jpg

C'est quand même un peut plus lisible que ce qu'on peut trouvé parfois.



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[] = "=";

    int vieRestante;
    float posi;
    char ami_3[32];
    string ville_2;

    getInfoInFile(path, "r", vieRestante);
    getInfoInFile(path, "%s", ville_2);
    getInfoInFile(path, "0", posi);
    getInfoInFile(path, "=", ami_3);

    cout <<"r" << vieRestante << endl;
    cout << "a" << ville_2 << endl;
    cout << "%s %s\n" << posi << endl;
    cout << "Temp" << ami_3 << endl;

    return 0;
}

ce que ça nous dis:

http://i27.servimg.com/u/f27/11/17/17/45/resuta10.jpg

BINGO! smile




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[] = "=";

    int vieRestante = 15;
    float posi = 155.789;
    char ami[32] = "r";
    string ville = "%s";

    saveInfoInFile(path, "0", vieRestante);
    saveInfoInFile(path, "=", posi);
    saveInfoInFile(path, "r", ami);
    saveInfoInFile(path, "a", ville);
    //test a la mano
    saveInfoInFile("%s %s\n", "Temp", "w+");

    return 0;
}

voici maintenant à quoi ressemble notre fichier de sauvegarde:

http://i27.servimg.com/u/f27/11/17/17/45/resuta11.jpg
Vous pouvez comparer; c'est impec smile



POUR FINIR


INFO
Info

On peut très bien rajouté des commentaires à notre fichier, cela ne pose aucun problème.
ATTENTION
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.

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.

#1 

09-09-2010 03:15:55

nico
Webmaster
Date d'inscription: 07-08-2009
Messages: 563
Corrections: 9

Salut nabouill, merci pour ce nouveau tuto smile 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 sad je pense que ça serait plus simple avec un seul get et un seul save (facile à dire tongue), quite à créer une petite classe suplémentaire et/ou un template pour gerer les types, qu'en penses-tu ?

Hors ligne


#2 

09-09-2010 08:02:58

nabouill
Abonné
Date d'inscription: 17-09-2009
Messages: 242
Corrections: 1

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 sad  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


#3 

09-09-2010 22:10:12

nico
Webmaster
Date d'inscription: 07-08-2009
Messages: 563
Corrections: 9

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>("=");
std::string maVille_2 = finder.getInfo<std::string>("r");


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


#4 

09-09-2010 22:49:48

TUpac
Habitué
Date d'inscription: 08-09-2009
Messages: 387
Corrections: 1

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 smile

Dernière modification par TUpac (09-09-2010 22:50:21)


"Si vous ne partagez pas votre stabilité avec les pauvres, les pauvres partageront leur instabilité avec vous."

Hors ligne


#5 

10-09-2010 00:20:21

nico
Webmaster
Date d'inscription: 07-08-2009
Messages: 563
Corrections: 9

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


#6 

10-09-2010 16:30:58

nabouill
Abonné
Date d'inscription: 17-09-2009
Messages: 242
Corrections: 1

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/1119 … lates-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 tongue )

Hors ligne


#7 

10-09-2010 16:35:38

nico
Webmaster
Date d'inscription: 07-08-2009
Messages: 563
Corrections: 9

Hors ligne


#8 

11-09-2010 01:12:50

nabouill
Abonné
Date d'inscription: 17-09-2009
Messages: 242
Corrections: 1

houhouuu !! je pense avoir trouvé la solution au probleme de convertion
http://cpp.developpez.com/faq/cpp/?page … 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("=");

int i;
finder.getInfo("r", i);

std::string texte;
finder.getInfo("%s", texte);

finder->drop();

ce qui oblige aussi a appelé la méthode drop() à la fin pour fermer le fichier

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[] = "=";

int i;
getInfo(path, "r", i);

std::string texte;
getInfo("%s", 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.

Dernière modification par nabouill (11-09-2010 09:49:01)

Hors ligne


#9 

11-09-2010 15:51:37

nico
Webmaster
Date d'inscription: 07-08-2009
Messages: 563
Corrections: 9

Là faut demander à Magun c'est lui l'expert wink

Hors ligne


#10 

11-09-2010 16:36:24

nabouill
Abonné
Date d'inscription: 17-09-2009
Messages: 242
Corrections: 1

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é.

Dernière modification par nabouill (11-09-2010 16:36:59)

Hors ligne


#11 

12-09-2010 00:49:13

nabouill
Abonné
Date d'inscription: 17-09-2009
Messages: 242
Corrections: 1

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 tongue
Je dis merci de m'avoir mis sur la piste des template.

Hors ligne


#12 

12-09-2010 01:13:42

nico
Webmaster
Date d'inscription: 07-08-2009
Messages: 563
Corrections: 9

cool, d'un coup c'est beaucoup plus agréable à lire wink

Hors ligne


#13 

06-01-2011 13:57:09

mmorpglefilm
Abonné
Lieu: la galaxie UDFy-38135539
Date d'inscription: 18-05-2010
Messages: 162
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 !!!

Dernière modification par mmorpglefilm (29-03-2013 01:52:02)

Hors ligne


#14 

25-02-2011 20:42:05

mmorpglefilm
Abonné
Lieu: la galaxie UDFy-38135539
Date d'inscription: 18-05-2010
Messages: 162
Site web

Et aux faite, il y a une licence sur ton code ? Il est gratuit ?

Merci de me répondre.

Hors ligne


#15 

27-02-2011 00:45:27

nabouill
Abonné
Date d'inscription: 17-09-2009
Messages: 242
Corrections: 1

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


Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
Analysé par
872 membres
1422 sujets
11104 messages
Dernier membre inscrit: Glider
19 invités en ligne
Aucun membre connecté
RSS Feed