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

C++ Discussion :

Extraire une chaine de caractères depuis un fichier txt


Sujet :

C++

  1. #1
    Membre à l'essai
    Inscrit en
    Février 2010
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 27
    Points : 19
    Points
    19
    Par défaut Extraire une chaine de caractères depuis un fichier txt
    Bonjour

    j'ai un petit problème avec la lecture d'un fichier, j'ai un fichier config.conf dans le quel il est stocké des paramètres d’authentification, après quelques recherches je suis arriver à extraire une ligne entière du fichier, sauf que moi j'ai besoin de code'extraire qu'une partie de la ligne.

    voici le code :
    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
     
    #include <iostream>
    #include <string>
    #include <fstream>
     
    using namespace std;
     
    int main()
    {
         ifstream fichier("config.conf", ios::in);  // on ouvre en lecture
     
         if(fichier)  // si l'ouverture a fonctionné
         {
     
             string ligne;
             while(getline(fichier, ligne))  
             {
                 cout << ligne << endl;  // on l'affiche
             }
     
             fichier.close();
         }
         else
             cerr << "Impossible d'ouvrir le fichier !" << endl;
     
         return 0;
    }

    voici le résultat :

    $*./rd_file*
    MYSQLHOST="localhost"
    MYSQLPASS="SwPass"
    MYSQLUSER="sw"
    MYSQLDB="swbase"


    Alors j'aimerai savoir comment faire pour extraire uniquement :
    - localhost
    - SwPass
    - sw
    - swbase

    Merci pour votre aide.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    le plus simple serait, je pense, d'interpréter chaque ligne ainsi extraite.
    Dans le cas où le début de ligne vaut MYSQLHOST, tu extraies ce qui suit.
    Ca doit pas être compliqué à l'aide des substr puisque tu connais chaque "mot-clé" de début de ligne, la longueur d'une ligne et que la syntaxe est toujours identique (mot-clé="valeur") .
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre à l'essai
    Inscrit en
    Février 2010
    Messages
    27
    Détails du profil
    Informations forums :
    Inscription : Février 2010
    Messages : 27
    Points : 19
    Points
    19
    Par défaut
    Donc tu veux dire que je dois extraire toute la ligne dans une variable, puis extraire la string que je veux à part???

  4. #4
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par milanoran Voir le message
    Donc tu veut dire que je dois extraire toute la ligne dans une variable, et puis extraire la string que je veut appart ???
    Hélas, oui. Toute autre manière nécessite un parseur nettement plus évolué que celui que tu souhaites écrire.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  5. #5
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 965
    Points
    32 965
    Billets dans le blog
    4
    Par défaut
    Tu réalises déjà la moitié de l'opération en lisant la ligne, il te reste à définir des index délimitant la valeur à extraire.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    Dans l'absolu, il faut te dire que, si le fichier prend la forme d'une paire "clé / valeur", c'est qu'il y a très certainement une raison...

    Cette raison est *relativement* simple : chaque valeur va être utilisée pour représenter une chose bien particulière.

    Si l'on suit l'exemple que tu donne, la première représente l'hote à contacter, la seconde le mot de passe, la troisième le nom d'utilisateur et la quatrième la base de données à utiliser

    Ces données vont être utilisées en différents endroits, et l'on peut donc envisager deux possibilités:

    La première est de se dire que l'on va stocker ces valeurs séparément, et que l'on n'a donc pas besoin de maintenir cette correspondance clé / valeur.

    Ce n'est pas forcément incorrect, mais il faut te dire que cela risque de compliquer énormément la récupération des valeurs, en rendant quasi impossible la création dynamique de requêtes.

    La deuxième est de te dire que ces informations sont des informations clairement identifiées (par la clé qui les représente ) qui seront utilisées en conjonction avec une foule d'autres données clairement identifiées (par une clé de même nature) pour créer des requêtes particulières, et qu'il est donc *peut etre* intéressant de créer une collection de ces paires "clé / valeur" de manière à pouvoir, tout simplement, obtenir la valeur en recherchant la clé

    cela aurait l'énorme avantage de permettre d'obtenir n'importe quelle valeur en utilisant exactement le même code, à l'exception d'un paramètre dont la valeur de la donnée que l'on souhaite obtenir, et qui prendrait la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    std::string host = maCollection.find("MYSQLHOST");
    std::string table = maCollection.find("laTableMachin");
    std::string db = maCollection.find("MYSQLDB");
    /*...*/
    Sans pour autant interdire le fait d'avoir une classe qui se présenterait sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class MyClass
    {
        public:
           std::string const & getHostName() const{return maCollection.find("MYSQLHOST");}
           std::string const & getdbName() const{return maCollection.find("MYSQLDB");}
           std::string const & getUserName() const{return maCollection.find("MYSQLUSER");}
        /*...*/
    };
    Tu me demanderas sans doute quel intérêt tu aurais à le faire

    Et bien, il est simple : il serait d'utiliser une granularité plus fine dans les responsabilités :

    Tu aurais, d'un coté, une classe dont la responsabilité est de gérer (chargement, sauvegarde, fourniture et modification ) les données et d'autres classes qui se contenteraient de... manipuler ces données.

    En effet, un gestionnaire de connexion n'a aucunement besoin de maintenir ces données : il n'en a besoin que lors de la création de la connexion (connexion, reconnexion, changement de base de donées)...

    Par la suite, il peut se contenter de maintenir... la connexion ouverte

    Mais on peut aussi envisager le fait que, tot ou tard, tu veuilles donner la possibilité de choisir l'hote, la base de données, l'utilisateur ou le mot de passe!

    Il ne serait pas logique d'accéder à ces informations en lecture (et encore moins en modification!!) au travers du gestionnaire de connexion !!! Ce n'est, tout simplement, pas son rôle que de s'occuper de ces choses là

    De plus, j'entrevois deux problèmes à ne vouloir récupérer que les valeurs et non les clés:

    Le premier, c'est que tu devras soit te dire que le fichier fournira toujours les paires dans le même ordre, et tu n'en as malheureusement aucune certitude.

    Le second, c'est que, si tu accepte l'idée que les données ne seront pas toujours fournies dans le même ordre, tu devras systématiquement tester dans ta boucle de récupération la valeur de toutes les clés possibles, et que :
    1. la comparaison de chaine prend énormément de temps
    2. cela va à l'encontre de l'OCP (Open Close Principle) : si, un jour, tu décide de rajouter une valeur à ton fichier, tu devras modifier ta fonction de lecture pour la prendre en compte
    Bref, tout ce laïus a pour seul but de te faire comprendre que, étant donné que l'on dispose, de toutes façons, d'une paire clé / valeur, il est très certainement intéressant de récupérer (au moins temporairement) l'ensemble des clés et l'ensemble des valeurs correspondantes

    De cette manière, si tu décides, un jour, d'ajouter une pair clé / valeur à ton fichier, tu n'auras absolument rien à modifier pour qu'elle soit prise en compte (comprend : ni la lecture ni les objets qui n'ont pas besoin de cette nouvelle valeur ne devront être modifiés ) et les objets qui auront besoin de cette nouvelle valeur pourront y accéder

    il existe alors deux solutions : une "lente" (à cause des conversions qu'elle implique) mais facile, qui va utiliser presque tout ce que tu utilise déjà en rajoutant ... la classe stringstream, qui est un flux de conversion et qui pourrait ressembler à

    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
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <map>
     
    using namespace std;
     
    int main()
    {
        /* on crée une map (un conteneur associatif trié ) qui nous servira
         * à maintenir les paires clé / valeurs :D
         */
        std::map<std::string, std::string> datas;
        { // crée une portée à l'intérieur qui aura pour but de 
          // "forcer" la destruction de la variable fichier une fois la lecture
          // finie
             ifstream fichier("config.conf");  // ios::in est implicite pour un fichier
                                               // d'entrée (ifstream) ;)
     
             if(fichier)  // si l'ouverture a fonctionné
             {
                 string ligne;
                 while(getline(fichier, ligne))  
                 {
                       /* on introduit la ligne récupérée dans un flux de conversion
                        */
                      std::stringstream ss;
                      ss << ligne;
                      /* on va récupérer deux chaines de caractères : la clé, et la 
                       * valeur
                       */
                      std::string key;
                      std::string value;
                     getline(ss,key,'='); // le troisième paramètre représente le
                                          // caractère à utiliser comme séparateur
                     getline(ss,value);
                     datas.insert(std::make_pair(key,value);
                 }
     
                /* fichier.close(); inutile : le fichier est automatiquement
                 * fermé quand la variable est détruite
                 */
             }
             else
                 cerr << "Impossible d'ouvrir le fichier !" << endl;
        }
        /* nous pouvons afficher toutes les paires récupérées */
        for(std::map<std::string,std::string>::const_iterator it =datas.begin();
            it!=data.end();++it)
        {
            cout<<"clé = "it->first<<" valeur = "<<it->second <<std::endl;
        } 
         return 0;
    }
    La deuxième est un peu plus rapide (après tout, il ne s'agit pas de récupérer des milliers de paires ) parce qu'elle évite les conversions mais sans doute aussi plus complexe, et va, tout simplement utiliser les fonctions find et substr disponibles dans la classe string.

    Elle pourrait ressembler à

    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
    #include <iostream>
    #include <string>
    #include <fstream>
    #include <map>
     
    using namespace std;
     
    int main()
    {
        /* on commence de la même manière :D 
         */
        std::map<std::string, std::string> datas;
        { 
             ifstream fichier("config.conf");
     
             if(fichier) 
             {
                 string ligne;
                 while(getline(fichier, ligne))  
                 {
                      /* utilisation de
                       * size_t string.find( 'char_to_find', size_t pos = 0)
                       * renvoie l'index du premier caractère correspondant à
                       * char_to_find trouvé dans la chaine de caractères 
                       * et se trouvant au minimum à la position pos ou
                       * string::npos s'il n'existe pas
                       *
                       * utilisation de
                       * string string.substr(size_t pos = 0, size_t size = std::npos)
                       * renvoie la sous chaine commencant à l'index pos 
                       * et composée d'un nombre de caractères équivalent au
                       * résultat de std::max(size, string.size()-index_begin)
                       */
                      std::string key=ligne.substr(0,ligne.find('=') - 1);
                      std::string valueligne.substr(ligne.find('=')+1);
                     datas.insert(std::make_pair(key,value);
                 }
             }
             else
                 cerr << "Impossible d'ouvrir le fichier !" << endl;
        }
        /* ... comme plus haut :D */
         return 0;
    }
    Mais ces deux codes (qui ne sont que l'adaptation du tien ) présentent un gros problème : la fonction main() commence à en faire beaucoup trop

    Il existe en effet un principe dit "de la responsabilité unique" qui conseille de faire en sorte que toute chose (que ce soit une classe ou une fonction) n'ait qu'une seule responsabilité, mais qu'elle s'en acquitte correctement.

    Or, actuellement, la fonction main() a trois responsabilités :
    1. lire un fichier
    2. scinder une chaine en pair clé / valeur
    3. afficher la clé et la valeur
    C'est beaucoup trop!!!

    Chaque responsabilité devrait être prise en charge par une fonction particulière

    Nous en arriverions donc à quelque chose comme
    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
     
    /* typedefs pour se faciliter la vie :D */
    typedef std::pair<std::string, string> pairstring;
    typedef std::map<pairstring> datamap;
    typedef typename datamap::const_iterator dataiterator;
     
    /* scinde la chaine fournie en paramètre  par rapport au caractère "=" 
     * qu'elle contient et renvoie une pair "clé / valeur" correspondant aux
     * deux chaines récupérées
     */
    pairstring splitString(std::string const & str)
    {
        /* je vais utiliser les stringstream, c'est plus facile :D */
        std::string key;
        std::string value;
        std::stringstream ss;
        ss<<str;
        std::getline(ss,key,'=');
        std::getline(ss,value);
        return pairstring(key,value);
    }
    /* lit le fichier dont le nom est fournis en paramètre et remplis la map 
     * elle aussi fournie
     *
     * lance une exception si le fichier n'existe pas
     */
    void readFile(std::string const & filename, datamap & datas
    {
        std::ifstream ifs(filename.c_str());// en C++11 on pourrait écrire ifs(filename) ;)
        if(!ifs)
        {
            std::string error = "le fichier ";
            error.append(filename)
                   .append(" n'existe pas");
            throw std::runtime_error(error);
        }
        /* arrivés ici, on sait que le fichier existe */
        std::string line;
        while(std::getline(ifs, line)
        {
            pairstring pair = splitString(line);
            datas.insert(pair);
        }
    }
    /* affiche les clés et les valeurs comprises entre begin et end */
    void print(dataiterator begin, dataiterator end)
    {
        while(begin != end)
        {
            cout<<"clé = "it->first<<" valeur = "<<it->second <<std::endl;
            ++begin;
        }
    }
    /* et enfin la fonction main() */
    int main()
    {
        datamap datas;
        /* comme la lecture risque de lancer une exception */
        try
        {
            readFile(config.conf, datas);
            print(datas.begin(),datas.end);
        }
        catch(std::runtime_error & e)
        {
            std::cout<<e.what();
            return 1;
        }
        return 0;
    }
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. [Débutant] Trouver la position d'une chaine de caractère dans un fichier .txt
    Par martineaston dans le forum MATLAB
    Réponses: 4
    Dernier message: 19/06/2013, 14h14
  2. Réponses: 3
    Dernier message: 25/08/2011, 10h26
  3. Réponses: 2
    Dernier message: 13/02/2011, 10h08
  4. [VB]Recherche d'une chaine de caractère dans un fichier
    Par Empty_body dans le forum VB 6 et antérieur
    Réponses: 26
    Dernier message: 16/02/2006, 09h10
  5. Ecriture d'une chaine de caractères dans un fichier
    Par fleur_de_rose dans le forum MFC
    Réponses: 6
    Dernier message: 10/02/2006, 09h34

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