IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Autres éditeurs Discussion :

ifstream, dérivation ou encapsulation ?


Sujet :

Autres éditeurs

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2006
    Messages : 27
    Par défaut ifstream, dérivation ou encapsulation ?
    Salut à tous et bonne année !

    Voila, j'ai un petit problème, petit car je pourrai passer par des moyens détourné. Je vous explique, j'ai un fichier configuration à charger, simple fichier texte listant un certain nombre de données dans un certain ordre. J'aimerais y ajouter des commentaires, précédé d'un '#' par exemple. Jusque là, il n'y a pas de soucis.

    Oui, mais voila, je voudrai que ça soit complètement transparent à l'utilisation, je souhaiterai pouvoir faire des "file>>value" qui ignorerai automatiquement les commentaires. Pour cela, deux solutions me sont venus à l'esprit, l'encapsulation tout d'abord ou la dérivation ensuite.

    la dérivation par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class CFileConfig : public std::ifstream
    {
    public:
    	CFileConfig(const char * filename):std::ifstream(filename){};
    	~CFileConfig();
    };
     
    /* plus loin */
    CFileConfig file("file.txt");
    int var1,var2;
    file>>var1>>var2;

    Mon problème est donc surdéfinir l'opérateur >> afin qu'il garde la capacité d'écrire dans n'importe quel type de donnée tout en ignorant les lignes commencant par un #...


    Merci d'avance pour vos pistes

    Edit: Oula, je viens de me rendre compte que ce message risque de ne pas être au bon endroit…

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    865
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 865
    Par défaut
    Tu dois composer. En effet, un ifstream est un basic_ifstream<char> et basic_ifstream dérive de basic_istream. Dans ces deux classes, aucune méthode n'est virtuelle en particulier les opérateurs >> (sauf le destructeur de basic_istream).
    Tu ne pourras donc pas profiter des mécanismes de virtualité.
    Tu dois donc composer et non dériver.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2006
    Messages : 27
    Par défaut
    J'avais constaté effectivement, mais qu'entend tu par "composer", je réécris tout les opérateurs >> ?

    Sinon, j'avais peut être pensé à, à l'ouverture du fichier, lire et recréer un flux sans les commentaires et donc finalement ouvrir ce dernier, une voie à explorer? Je vais essayer...

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2006
    Messages : 27
    Par défaut
    Donc voila, en définitive je suis passé par un fichier temporaire

    FileConfig.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    #define TEMP_EXT ".temp"
    class CFileConfig : public std::ifstream
    {
    public:
    	CFileConfig(const char * filename);
    	~CFileConfig();
    private:
    	char * m_tempFileName;
     
    };
    FileConfig.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    CFileConfig::CFileConfig(const char * filename):std::ifstream(filename)
    {
    	/* save temporary file name */
    	m_tempFileName = new char[strlen(filename) + strlen(TEMP_EXT)];
    	strcpy(m_tempFileName,STR.sprintf_rs("%s"TEMP_EXT"",filename));
     
    	std::ofstream tempFile(m_tempFileName);
    	ASSERT(tempFile);
     
    	/* copy the file in new file */
    	std::string line;
    	while(std::getline(*this, line))
    	{
    		if(line.length()>0)
    			if(line[0]!='#')
    				tempFile<<line<<std::endl;
    	}
     
    	/* close previous file, open temparary file */
    	this->close();
    	this->clear();
    	tempFile.close();
    	this->open(m_tempFileName);
    }
     
    CFileConfig::~CFileConfig()
    {
    	this->close();
    	this->clear();
    	/* delete temporary file */
    	std::remove(m_tempFileName);
    }

    Petite note : mon "STR.sprintf_rs" est en fait un sprintf sur des chaines de caractères temporaires.

    Qu'en pensez vous ? C'est vrai que ça fait une ecriture de fichier, mais c'est une opération réalisé uniquement au chargement... D'ailleurs, je n'ai pas trouvé un type ofstream qui n'ecrirai pas dans un fichier mais seulement en memoire...

  5. #5
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    865
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 865
    Par défaut
    J'ai retravaillé ton code ainsi pour composer avec un ifstream. Vu ce que tu en fait, il est inutile de le dériver (en plus des raisons que j'ai dit hier).
    Par contre, je ne suis pas pourquoi tu rouvres ton fichier en lecture seul à la fin. Tu ne peux plus rien écrire dedans... J'aurais plutôt composé avec un ofstream. A vrai dire, je ne vois pas ce que tu veux faire, pourquoi tu parles d'un ofstream qui n'écrirait qu'en mémoire...

    Autrement,
    • éviter les char * en C++, alors qu'on dispose des strings, ça évite d'utililiser les strcpy, de se planter dans les tailles de chaines et d'écrire un STR.sprintf_rs inutile
    • éviter les macros en C++, préférer déclarer des constantes
    • pas de macro ASSERT sur une erreur qui peut toujours apparaître à l'exécution, en effet, une fois que ton assert sera désactivé (par NDEBUG), tu ne pourras plus détecter une erreur d'ouverture de fichier


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
     
    #include <cassert>
    #include <fstream>
    #include <iostream>
    #include <stdexcept>
    #include <string>
     
    const std::string TEMP_EXT = ".temp";
     
    class invalid_file : public std::runtime_error {
    public:
      explicit invalid_file (const std::string & args);
    };
     
    invalid_file::invalid_file (const std::string & args) 
      : std::runtime_error (args) {}
     
    class CFileConfig {
    public:
      CFileConfig(const std::string &);
      ~CFileConfig();
    private:
      std::ifstream m_configFile;
      std::string   m_tempFilename;
    };
     
    CFileConfig::CFileConfig(const std::string & filename) 
      : m_configFile (filename.c_str()),
        m_tempFilename (filename + TEMP_EXT) {
     
      if ( ! m_configFile ) throw invalid_file ("Can't open " + filename);
     
      std::ofstream tempFile(m_tempFilename.c_str());
     
      if ( ! tempFile ) {
        m_configFile.close ();
        throw invalid_file ("Can't open " + m_tempFilename);
      }
     
      /* copy the file in new file */
      std::string line;
      while(std::getline(m_configFile, line)) {
        if(line.length() > 0) {
          if(line[0] != '#') {
    	tempFile << line << std::endl;
          }
        }
      }
     
      /* close previous file, open temporary file */
      m_configFile.close ();
      tempFile.close ();
      m_configFile.open (m_tempFilename.c_str());
     
    }
     
    CFileConfig::~CFileConfig() {
      m_configFile.close();
      /* delete temporary file */
      std::remove(m_tempFilename.c_str());
    }
     
    int main () {
      try {
        CFileConfig config ("if.txt");
      } catch (std::exception & ex) {
        std::cerr << ex.what () << std::endl;
      }
      return 0;
    }

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : Canada

    Informations forums :
    Inscription : Avril 2006
    Messages : 27
    Par défaut
    Merci pour cette correction, à vrai dire, à propos de l'utilisation des char * préaloué, je les utilise car je tourne sur un système très limité (une espèce de console portable) où les std::string sont assez lent.

    De la même manière, les exceptions me paraissent plus sûre mais encore une fois, pour un soucis d'optimisation, je me suis réduit aux asserts (d'autant plus que c'est imposé par mon cahier des charges)...

    Sinon, j'ouvre en lecture seule car je n'ai justement pas besoin de réécrire dans le fichier. Enfin, je parlais d' "ofstream en memoire" en esperant trouver un équivalent de ofstream qui n'écrirai pas sur le disque dur, je ne sais pas vraiment si cela existe...

    En tout cas, merci beaucoup pour ces indications! ça me libère du flou dans lequel je navigue parfois en utilisant std...

    EDIT : Deux petites erreures dans mon code d'origine qui est loin d'être une référence d'ailleurs :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    m_tempFileName = new char[strlen(filename) + strlen(TEMP_EXT) + 1];
    /* in the destuctor */
    delete [] m_tempFileName;

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 31
    Dernier message: 30/03/2006, 16h57
  2. Probleme avec ifstream
    Par maitre hibou dans le forum SL & STL
    Réponses: 12
    Dernier message: 12/05/2004, 13h33
  3. Encapsulation graphique d'un outil en ligne de commande
    Par Leishmaniose dans le forum Composants VCL
    Réponses: 3
    Dernier message: 12/11/2003, 11h59
  4. Dérivation dune fonction
    Par srvremi dans le forum C++Builder
    Réponses: 2
    Dernier message: 27/07/2002, 14h21
  5. [MFC](encapsulation ADO) ou placer le code
    Par philippe V dans le forum MFC
    Réponses: 2
    Dernier message: 13/06/2002, 14h58

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo