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

SL & STL C++ Discussion :

problème avec std::string:find()


Sujet :

SL & STL C++

  1. #1
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut problème avec std::string:find()
    Hoy!

    Un problème plus ou moins simple que je n'arrive pas vraiment à résoudre.

    J'ai écris une série de fonction afin de pouvoir récupérer des paramètres d'une chaine formatée. Ces paramètre peuvent être des float, des bool, des string, etc. Les chaines sont stockées dans un fichier et séparées par un saut de ligne. Je sais quel est le type de tel paramètre à telle endroit.

    J'ai écris quelques fonction pour récupérer ses paramètres, que voici :

    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
     
    	// J'ouvre le fichier qui contient
    	std::stringstream URL;
    	URL << "game/stage" << stage_number;  // Stage number est int, d'où l'utilisation du stringstream
    	std::ifstream file(URL.str().c_str());
     
    	std::string data; // Le string qui va me servir à stocker ma chaine
     
    	// Je récupère la chaine d'un fichier que je parcours avec std::getline
    	while(std::getline(file, data))
    	{
    		std::string label;
    		int pos = 0; // Le curseur qui va me permettre de parcourir ma chaine
    		pos = data.find(":"); // On recupère le premier paramètre, séparé par un ":"
    		label = data.substr(0, data.find(":")); // stockage du premier parametre
    		// En réalité, je fais un switch avec le label récupéré, via un std::map, et pour chaque label, je traite la ligne différement. Je met ici les lignes où il y a un bug
     
    		std::string ennemy_name;
    		float time, x, y, angle;
    		ennemy_name = GetParameter(pos, data);  // On récupère le parametre
    		NextParameter(pos, data);  // On passe au suivant
    		time = static_cast<float>(std::atof(GetParameter(pos, data).c_str()));  // On récpère le parametre
    		NextParameter(pos, data); // On passe au suivant, etc...
    		x = static_cast<float>(std::atof(GetParameter(pos, data).c_str()));
    		NextParameter(pos, data);
    		y = static_cast<float>(std::atof(GetParameter(pos, data).c_str()));
    		NextParameter(pos, data);
    		angle = static_cast<float>(std::atof(GetLastParameter(pos, data).c_str())); // On récupère le dernier paramètre, délimité par le caractère "saut de ligne"
     
    		std::cout << "Ennemy loaded, pos = " << pos <<", ennemy_name = " << ennemy_name << ", temps = " << time << ", x = " << x << ", y = " << y << ", angle = " << angle << std::endl; // Affichage du résultat
    	}
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    //  Fonction pour placer le curseur sur le paramètre suivant
    void Stage::NextParameter(int &pos, std::string &data)
    {
    	int temp = pos + 1;
    	pos = data.find('|', temp);
    	std::cout << "pos = " << pos << std::endl;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    // Fonction pour lire le paramètre actuel, délimité par un "|"
    std::string Stage::GetParameter(int pos, std::string &data)
    {
    	return data.substr(pos+1, data.find('|') - pos - 1);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    // Fonction pour lire le dernier paramètre, délimité par un "saut de ligne"
    std::string Stage::GetLastParameter(int pos, std::string &data)
    {
    	return data.substr(pos+1, data.find('\n') - pos - 1);
    }
    Voila, à partir de ce code, je lis donc mon fichier. Dans mon fichier, j'ai ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ENNEMY:megathron|7.0|250.0|-200.0|270.0
    ENNEMY:megathron|8.0|100.0|-200.0|270.0
    ENNEMY:megathron|9.0|500.0|-200.0|270.0
    Et mes std::cout m'affiche ceci :
    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
     
    pos = 16
    pos = 20
    pos = 26
    pos = -1   // Problème ! La position devrait être à 30 et des poussières
    Ennemy loaded, pos = -1, ennemy_name = megathron, temps = 7, x = 250, y = -200, angle = 0
    pos = 16
    pos = 20
    pos = 26
    pos = -1   // Pareil
    Ennemy loaded, pos = -1, ennemy_name = megathron, temps = 8, x = 100, y = -200, angle = 0
    pos = 16
    pos = 20
    pos = 26
    pos = -1  // Pareil
    Ennemy loaded, pos = -1, ennemy_name = megathron, temps = 9, x = 500, y = -200, angle = 0
    Le bug viens sur les derniers parametres, qui se retrouve avec une position -1. Si jme souviens bien, c'est la valeur de retour lorsque le std::string::find() ne trouve pas le caractère souhaité, pourtant, à cette position, le caractère est présent dans les lignes qui suivent.

    Si vous avez une idée, je suis preneur.

    (Au passage, si vous avez une proposition à me faire pour améliorer ma récupération de paramètre, par un autre objet de la bibliothèque standard ou quoi, je suis aussi preneur. :p)

  2. #2
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Bon en fait j'ai foiré le formatage de mon fichier lorsque je l'ai écris, j'ai corrigé ça remarche..

    Donc ya plus de problème.

    Par contre, si quelqu'un connais toujours une solutions plus élégante que ce que j'ai fait, je suis toujours preneur.

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

    Informations professionnelles :
    Activité : aucun

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

    J'ai, quelque part, l'impression que tu cherches midi à quatorze heures...

    en effet, si une ligne est d'office composée sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    string<pipe>float<pipe>float<pipe>float<pipe>float
    tu peux te "contenter" de récupérer le tout depuis un stringstream presque d'une seule traite, sous la forme de
    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
    while(std::getline(file,data)) /* la boucle de lecture */
    {
        /*zou, travaillons avec un stringstream */
        std::stringstream ss(data);
        /* il nous faut:
               - une chaine (premiers parametre)
               - 4 réels (4 dernier parametre)
               - un caractère(le séparateur)
         */
        std::string name;
        float p1;
        float p2;
        float p3;
        float p4;
        char c;
        /* la chaine nous embête, récupérons la en premier */
        if(!std::getline(ss,name,'|') )
            throw BadFormat("string");
        /* puis, récupérons les réels (vérification inside)*/
        if(! (ss>>p1>>c>>p2>>p3>>p4) )
            throw BadFormat("v1|v2|v3|v4");
        /* suite du traitement (sans doute la création d'un objet et l'ajout
         * dans la structure qui les gère)
         */
     
    }

  4. #4
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Je me suis taté à utiliser le stringstream avec getline() car le premier caractère séparateur de ma chaine est volontairement un : pour que, via le switch, je sache de quel genre d'objet et donc de quel genre de paramètres j'ai à faire. :/

    Donc c'est plus sous la forme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    string<pipe1>float<pipe2>float<pipe2>float<pipe2>float
    Avec un algo du genre :
    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
     
    - création du stringstream data;
    while(std::getline(file, data))
    {
        - récupération du premier paramètre limité par ":"
        switch(premier parametre)
        {
            case 1:
                - création des variables de stockage;
                - récupération des paramètre en ignorant le premier;
                - création de l'objet;
                - break;
            case 2:
                - rebelotte avec d'autres paramètres
                - break;
        }
    }
    Je vois mal comment demander au getline d'ignorer le premier paramètre une fois récupéré. :\

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    Je vois mal comment demander au getline d'ignorer le premier paramètre une fois récupéré. :\
    Exactement comme je l'ai indiqué: en le passant en troisième paramètre de la commande getline

    Fais attention au fait qu'une chaine de caractères ne peut pas servir comme valeur pour un test à choix multiple (switch... case): seule les valeurs numériques entières peuvent servir

    De plus, les chaines de caractères ne sont réellement pas ce qui se fait de plus efficace en terme de temps de comparaison: la comparaison se fait "caractère par caractère" jusqu'à en trouver un qui varie d'une chaine à l'autre.

    Cela signifie que, sur une chaine commençant par A et l'autre par z, cela peut être rapide, mais, si deux chaines sont à ce point semblables que seul le dernier caractère est différent, la comparaison prendra énormément de temps

    L' *idéal* dans ce cas est peut être de travailler avec une énumération qui aplanira toutes ces difficultés:
    Tu peux envisager une énumération proche de
    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
    enum eMyTypeSelect
    {
        tsMonPremierType = 1,
        tsMonDeuxiemeType,
        tsMonTroisiemeType,
        tsMonQuatriemeType
        /* , ... */
    };
    /* et, du coup, passer sous une forme proche de */
    while(std::getline(file,data) );
    {
        std::stringstream ss(data);
        eMyTypeSelect select;
        char c;
        while( (ss>>select>>c) )
        {
             swith(select)
                 case tsMonPremierType:
                      /* blabla */
                      break;
                 case tsMonDeuxiemeType:
                      /* blabla */
                      break;
                 case tsMonTroisiemeType:
                      /* blabla */
                      break
                 /*...*/
                 default:
                     throw BadFormat("TypeInconnu")
        }
    }
    où "blabla" pourrait facilement être envisagé sous la forme de fonctions proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    TypeDeBase* createTypeReel(std::istream &ss)
    {
        type valeur_1;
        type valeur_2;
        type valeur_3;
        /*...*/
        type valeur_n;
        char c;
        if(!(ss>>valeur_1>>c>>valeur_2>>c>>valeur_3>>/*...*/>>valeur_n))
            throw BadFormat("v1|v2|v3/*...|*/vn");
        return new TypeReel(valeur_1,valeur_2,valeur_3/*,...*/,valeur_n);
    }
    (en considérant ici que les types réels héritent tous d'un type de base commun et que tu les gère de manière statique comme s'ils étaient de ce type commun)

    Bien sur, tu en viendrais à créer une fonction par type réel qu'il te faut créer

  6. #6
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Le fait de faire ss>>select>>c va retirer du flux le select et le c?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    oui, c'est bien le but

    Les opérateurs de flux renvoient une référence vers le flux qui est géré, ce qui permet d'enchainer un nombre quasiment illimité d'appels à l'opérateur.

    Ainsi, le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ss>>truc>>bidule>>machin>>brol>>chose;
    est-il, du point de vue du résultat obtenu du moins, strictement similaire au code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ss>>truc;
    ss>>bidule;
    ss>>machin;
    ss>>brol;
    ss>>chose;
    La seule différence réelle entre les deux, c'est que, si tu décide de créer un test sur base de ces deux code, le premier ne t'autorise qu'un test sur la réussite globale de la lecture des 5 valeur, alors que le deuxième te permet, si tu le souhaite, de tester la réussite après chaque lecture
    [EDIT]Après, il reste les éventuels arguments de facilité de relecture du code... mais le premier code pourrait être réécrit de manière lisible sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    /* remarque l'absence de point virgule et la non répétition de ss */
    ss >> truc
       >> bidule
       >> machin
       >> brol
       >> chose

  8. #8
    Membre du Club
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Juin 2006
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo

    Informations forums :
    Inscription : Juin 2006
    Messages : 122
    Points : 59
    Points
    59
    Par défaut
    Putin c'est magique! (Fallait que jla sorte celle là)

    La taille du code réduit à facile 90%.

    Merci pour tout ça, je connaissais pas du tout stringstream, bah ça pète bien. =p

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Spidyy Voir le message
    Putin
    Non, je n'en ai pas vu sur ce forum
    c'est magique!
    Le forum Harry Potter, c'est pas ici
    La taille du code réduit à facile 90%.
    Tout l'avantage de connaitre un tout petit peu ce qui est fourni par le standard :C
    Merci pour tout ça, je connaissais pas du tout stringstream, bah ça pète bien. =p
    Une petite précision: stringstream est en fait la mise en commun de ostringstream (flux de conversion de donnée en une chaine de caractères) et de istringstream (flux de conversion d'une chaine de caractères en donnée).

    Si j'ai utilisé la classe stringstream dans mes exemples, c'est principalement parce qu'elle nous permet un transfert des données dans les deux sens

    Ces trois classes font partie d'un ensemble que l'on appelle "les flux de données formatés", qui manipulent des données venant de différents endroits mais qui utilisent les même opérateurs.

    Tu connais surement plusieurs de ces classes:
    • cout qui est le flux de sortie standard (généralement la console à l'écran)
    • cin qui est le flux d'entrée standard (généralement le clavier)
    • ifsteram qui est le flux d'entrée depuis un fichier (que tu utilise d'ailleurs ici )
    • ofstream qui est le flux de sortie vers un fichier (quand tu veux écrire dans un fichier)

    Les flux istringstream, cin et ifstream permettent de récupérer une information

    Les flux ostringstream, cout et ofstream de leur envoyer une information

    Enfin, il existe une classe de basecommune à tous les flux de sortie: ostream et une autre classe de base commune à tous les flux d'entrée: istream

    C'est grâce à ces classe de base que les flux de sorties disposent de l'opérateur << qui permet de leur envoyer des donnée et que les flux d'entrée disposent de l'opérateur >> qui permet d'en récupérer des données.

    Il est donc même possible d'écrire des fonctions (ou de surcharger l'opérateur << ou l'opérateur >>) qui s'adapteraient quel que soit le flux (d'origine ou de destination) réellement manipulé

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

Discussions similaires

  1. Problème avec std::string
    Par Gobelins dans le forum Débuter
    Réponses: 8
    Dernier message: 03/01/2011, 08h57
  2. problème avec std::string
    Par haraelendil dans le forum Langage
    Réponses: 10
    Dernier message: 25/06/2010, 18h56
  3. Problème avec std::string
    Par Laughing Man dans le forum C++
    Réponses: 18
    Dernier message: 07/02/2008, 19h04
  4. Sale problème avec les strings et les fichiers
    Par acieroid dans le forum C++
    Réponses: 18
    Dernier message: 26/04/2006, 09h47
  5. Problème avec std::Vector
    Par mister3957 dans le forum SL & STL
    Réponses: 8
    Dernier message: 16/02/2006, 10h18

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