[Résolu] Parser INI

Parser INI [Résolu] - C++ - Programmation

Marsh Posté le 04-04-2006 à 23:01:39    

Salut les gens !
Je suis en train de développer un parser de fichier INI pour mon jeu et je rencontre un problème qui me turlupine :
Voici l'aspect du fichier :
 

Citation :


 
[Object]
nickname = Planet2
archetype = earth.mesh
pos = 0, 0, -70000
 
[Object]
nickname = Planet3
archetype = earth.mesh
pos = 50000, 980, 70000
 
[Object]
nickname = Planet4
archetype = earth.mesh
pos = 0, 2000, 70000
 
[Object]
nickname = Planet5
archetype = earth.mesh
pos = 0, -2000, 70000


J'arrive donc à analyser les 2 premiers blocs sans problème, seulement quand je passe au 3ème j'obtient un crash du programme, sans aucune expliquation (mis à part biensur les infos debugage windows mais je ne sais pas comment les utiliser), pas même dans le log (j'utilise OGRE).
 
Il plante en arrivant à la ligne 10, il lit correctement le nom de la variable (test dans le log ;) ) puis plante ensuite.
Voici le code, en balise spoiler pour éviter de bouffer trop de place et épuré de tout les commentaires devenus inutiles et des tests débug :
EDIT : la balise spoil marche pas comme je l'aurai voulu ^^
 

Code :
  1. entityCount = 0;
  2.     badEntityCount = 0;
  3.     bitParsed = 0;
  4.     internalBitParsed = 0;
  5.     char line[256];
  6.     std::string s;
  7.     LogManager::getSingleton().logMessage("Parsing entities.ini..." );
  8.     ifstream Entities("entities.ini",ios::in);
  9.     if(!Entities)
  10.         LogManager::getSingleton().logMessage("ERROR: File not found!" );
  11.     else
  12.     {
  13.         while(!Entities.eof())
  14.         {
  15.             Entities.getline (line, sizeof (line));
  16.             bitParsed += Entities.gcount();
  17.             s = line;           
  18.             if (s == "[Object]" )
  19.             {
  20.                 int lineNbr = 0;
  21.                 internalBitParsed = 0;
  22.                 bool foundNext = false;
  23.                 while (!foundNext)
  24.                 {
  25.                     Entities.getline (line, sizeof (line));
  26.                     internalBitParsed += Entities.gcount();
  27.                     s = line;
  28.                     internalBitParsed += 1;
  29.                     if (s == "" )
  30.                     {
  31.                         lineNbr -= 1;
  32.                     }
  33.                     if (s == "[Object]" )
  34.                     {
  35.                         internalBitParsed -= Entities.gcount();
  36.                         Entities.seekg (bitParsed, ios::beg);
  37.                         internalBitParsed -= 1;
  38.                         bitParsed += internalBitParsed;
  39.                         foundNext = true;
  40.                     }
  41.                     else
  42.                         lineNbr += 1;
  43.                     if (Entities.eof())
  44.                     {
  45.                         Entities.seekg (bitParsed, ios::beg);
  46.                         foundNext = true;
  47.                     }
  48.                 }
  49.                 String nickname;
  50.                 StringVector svNickname;
  51.                 String archetype;
  52.                 StringVector svArchetype;
  53.                 StringVector svVariable;
  54.                 StringVector svPos;
  55.                 StringVector svRot;
  56.                 String spos;
  57.                 Vector3 pos;
  58.                 Real rotX, rotY, rotZ;
  59.                 String srot;
  60.                 int lineParsed = 0;
  61.                 bool foundNickname = false;
  62.                 bool foundArchetype = false;
  63.                 bool foundPos = false;
  64.                 bool foundRotate = false;
  65.                 bool errorFound = false;
  66.                 std::string m;
  67.                 entityCount += 1;
  68.                 while (lineParsed != lineNbr)
  69.                 {
  70.                     Entities.getline (line, sizeof (line));
  71.                     s = line;
  72.                     svVariable = StringUtil::split(s, "=", 1);
  73.                     m = svVariable[0];
  74.                     if (m.find(" " ) != string::npos)
  75.                     {
  76.                         while (StringUtil::startsWith(m," " ))
  77.                             StringUtil::trim(m,true,false);
  78.                         while (StringUtil::endsWith(m," " ))
  79.                             StringUtil::trim(m,false,true);
  80.                     }
  81.                     LogManager::getSingleton().logMessage("Reads " + m);
  82.                     if ((m == "nickname" ) && (!foundNickname))
  83.                     {
  84.                         svNickname = StringUtil::split(s, "=", 1);
  85.                         m = svNickname[1];
  86.                         nickname = m;
  87.                         if (nickname.find(" " ) != string::npos)
  88.                         {
  89.                             while (StringUtil::startsWith(nickname," " ))
  90.                                 StringUtil::trim(nickname,true,false);
  91.                             while (StringUtil::endsWith(nickname," " ))
  92.                                 StringUtil::trim(nickname,false,true);
  93.                         }
  94.                         lineParsed += 1;
  95.                         foundNickname = true;
  96.                     }
  97.                     else if ((m == "archetype" ) && (!foundArchetype))
  98.                     {
  99.                         svArchetype = StringUtil::split(s, "=", 1);
  100.                         m = svArchetype[1];
  101.                         archetype = m;
  102.                         if (archetype.find(" " ) != string::npos)
  103.                         {
  104.                             while (StringUtil::startsWith(archetype," " ))
  105.                                 StringUtil::trim(archetype,true,false);
  106.                             while (StringUtil::endsWith(archetype," " ))
  107.                                 StringUtil::trim(archetype,false,true);
  108.                         }
  109.                         lineParsed += 1;
  110.                         foundArchetype = true;
  111.                     }
  112.                     else if ((m == "pos" ) && (!foundPos))
  113.                     {
  114.                         svPos = StringUtil::split(s, "=", 1);
  115.                         m = svPos[1];
  116.                         spos = m;
  117.                         svPos = StringUtil::split(spos, ",", 2);
  118.                         spos = StringConverter::toString(svPos);
  119.                         pos = StringConverter::parseVector3(spos);
  120.                         lineParsed += 1;
  121.                         foundPos = true;
  122.                     }
  123.                     else if ((m == "rotate" ) && (!foundRotate))
  124.                     {
  125.                         svRot = StringUtil::split(s, "=", 1);
  126.                         m = svRot[1];
  127.                         svRot = StringUtil::split(srot, ",", 2);
  128.                         rotX = StringConverter::parseReal(svRot[0]);
  129.                         rotY = StringConverter::parseReal(svRot[1]);
  130.                         rotZ = StringConverter::parseReal(svRot[2]);
  131.                         lineParsed += 1;
  132.                         foundRotate = true;
  133.                     }
  134.                     else if (m == "[Object]" )
  135.                     {
  136.                         LogManager::getSingleton().logMessage("## Error while parsing Entity" + StringConverter::toString(entityCount)
  137.                             + " : [Object] found, unexpected. (Breaking)" );
  138.                         break;
  139.                     }
  140.                     else if (m != "" )
  141.                     {
  142.                         LogManager::getSingleton().logMessage("## Error while parsing Entity" + StringConverter::toString(entityCount)
  143.                             + " : unknown parameter : \"" + m + "\". (Ignoring)" );
  144.                         lineParsed +=1 ;
  145.                     }
  146.                     if (Entities.eof())
  147.                         break;
  148.                 }
  149.                 if ((!foundNickname) || (!foundArchetype) || (!foundPos))
  150.                 {
  151.                     badEntityCount +=1 ;
  152.                     errorFound = true;
  153.                     if (!foundNickname)
  154.                         LogManager::getSingleton().logMessage("## Error while parsing Entity" + StringConverter::toString(entityCount)
  155.                                                                 + " : could not find nickname." );
  156.                     if (!foundArchetype)
  157.                         LogManager::getSingleton().logMessage("## Error while parsing Entity" + StringConverter::toString(entityCount)
  158.                                                                 + " : could not find archetype." );
  159.                     if (!foundPos)
  160.                         LogManager::getSingleton().logMessage("## Error while parsing Entity" + StringConverter::toString(entityCount)
  161.                                                                 + " : could not find position." );
  162.                 }
  163.                 if (!errorFound)
  164.                 {
  165.                     String NodeNickname = nickname + "Node";
  166.                     String EntityNickname = nickname;
  167.                     mSceneMgr->createEntity(EntityNickname, archetype);
  168.                     mSceneMgr->getRootSceneNode()->createChildSceneNode(NodeNickname, pos)->attachObject(mSceneMgr->getEntity(EntityNickname));
  169.                     if (foundRotate)
  170.                     {
  171.                         mSceneMgr->getSceneNode(NodeNickname)->pitch(Ogre::Degree(rotX));
  172.                         mSceneMgr->getSceneNode(NodeNickname)->yaw(Ogre::Degree(rotX));
  173.                         mSceneMgr->getSceneNode(NodeNickname)->roll(Ogre::Degree(rotX));
  174.                     }
  175.                 }
  176.             }
  177.         }
  178.         Entities.close ();
  179.         LogManager::getSingleton().logMessage("Found " + StringConverter::toString(entityCount) + " entities. (" + StringConverter::toString(badEntityCount) + " ignored)" );
  180.         LogManager::getSingleton().logMessage("Parsing done." );
  181.     }


 
Voilà :) !
Je sais qu'il existe deja pas mal de parser d'ini sur le net, seulement je n'arrive pas à comprendre leur code et je les trouve trop contraignant, pas assez en accord avec ce que je veux en faire ;) ! Et puis rien de mieu pour apprendre que d'écrire sois même.
Donc j'ai tout fait seul sans piquer de code dans un autre programme : je comprend ce que j'ai marqué :)
 
(PS : le temps qu'on active mon compte j'ai essayé un parser deja tout fait, qui règle donc mes problèmes, mais ca m'intéresse toujours :) )
 
si vous voyez d'où vient l'erreur ou si vous avez un conseil à me donner, je prend bien sur :)
 
J'ai aussi un problème avec la dernière section (si j'en suprime une, l'autre bug disparait mais j'ai celui ci qui arrive), il ne lit rien sur chaque ligne  :??:  !
J'ai ajouté une petite ligne de débug qui note dans le log ce que "lit" le programme à chaque ligne (LogManager::getSingleton().logMessage("Reads " + m);), et ca donne :
Reads  
Reads  
Reads  
Et ensuite j'ai mes erreur selon lesquelles ils ne trouve pas les variables indispensables, donc l'entité est ignorée et le jeu se lance sans problème (mis à part qu'il manque une planète :) ).
 
Ce dernier bug je l'ai depuis que j'ai commencé à programmer le parser, avec les différentes techniques (soit en ligne par ligne, soit en mot à mot, pas pratique cependant). Quand au premier, je l'ai de temps en temps mais les autres foi je trouvais une erreur quelque part ou une incohérence mais là... nada :(
 
Merci d'avance pour votre aide ;)


Message édité par Kaalith le 07-04-2006 à 18:34:25
Reply

Marsh Posté le 04-04-2006 à 23:01:39   

Reply

Marsh Posté le 04-04-2006 à 23:04:36    

C'est pour le fun ?
Parce qu'il y a des primitives windows pour ça ...
 
Sinon tu mélanges données et traitements, c'est pas très bon pour de l'objet.

Reply

Marsh Posté le 04-04-2006 à 23:10:11    

euh... tu connais les fonctions Win32 WritePrivateProfileString et GetPrivateProfileString ? [:pingouino]


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 04-04-2006 à 23:10:44    

Ben les primitives windows c'est bien joli mais niveau portabilité c'est pas terrible :)
Et sinon maintenant oui c'est pour le fun ^^
 
Je mélange données et traitements ? J'aimerais bien en savoir plus :P
 
PS : de plus comme marqué tout au debut de mon post, mes inis ont des groupes de même noms et je ne sais pas si c'est supporté par windaube (en tout cas, pas par le parser que j'ai essayé, dommage)

Message cité 1 fois
Message édité par Kaalith le 04-04-2006 à 23:16:04

---------------
- Kal
Reply

Marsh Posté le 04-04-2006 à 23:37:10    

Si c'est toi qui gère la création du fichier INI, t'aurais peut-ête tout intérêt à passer au XML non? Ca t'éviterait une bonne partie du traitement :)
 
Sauf si tu veux vraiment faire un parser INI bien sûr ;)

Reply

Marsh Posté le 05-04-2006 à 09:40:02    

INI c'est du windows de toutes façons, ça n'a pas à être portable.
 
Si tu veux du portables, XML est tout indiqué, effectivement.

Reply

Marsh Posté le 05-04-2006 à 09:43:21    

Kaalith a écrit :

Je mélange données et traitements ? J'aimerais bien en savoir plus :P


Code :
  1. else if (m == "[Object]" )


Ca c'est mélanger données et traitement. Autant écrire une fonction générique paramétrée par un nom de section qui te renvoie par exemple une map composée en clef des clés contenues dans la section associées à leurs valeurs. Dans ton code, on rajoute une section ton code ne le parse pas.
 
 

Kaalith a écrit :

PS : de plus comme marqué tout au debut de mon post, mes inis ont des groupes de même noms et je ne sais pas si c'est supporté par windaube (en tout cas, pas par le parser que j'ai essayé, dommage)


INI c'est du windows, voir les fonctions que Vladimir t'as refilé, ce sont les fonctions qui parsent le fichier pour toi, voir msdn.

Reply

Marsh Posté le 05-04-2006 à 10:31:19    

TinyXML

Reply

Marsh Posté le 05-04-2006 à 11:23:35    

+1

Reply

Marsh Posté le 05-04-2006 à 11:35:35    

Et pis en ini, [section] se doit d'etre unique il me semble.
Alors tes 4/5 [object]! c'est meme pas du ini portable sous windows :-)
Et pis quand je vois le code, ce n'est pas le code typique d'un parser...

Reply

Marsh Posté le 05-04-2006 à 11:35:35   

Reply

Marsh Posté le 05-04-2006 à 14:12:13    

Groumph ^^ Pourquoi le fait que l'extension soit INI change quoi que ce soit à la portabilité ;) ? L'essentiel est la façon de le parser.
Pour info, le jeu Freelancer utilise ce type de fichier INI pour quasiment tout (sauf les ressources son textures et models évidement). Il fonctionne sur le même principe (sections de même nom) et fonctionne sur mac :) Donc c'est portable le INI!
Mais je concède que c'est pas du INI généralisé, qu'on utilise dans les fichier de config. C'est du ini Orienté Objet :-)
 
Et désolé je ne veux pas utiliser la bouille XML :P C'est du chinoi, et vraiment pas sympa à éditer. Les inis sont super pour permettre à l'utilisateur de modifier le contenu du programme facilement et sans se prendre (trop) la tête.
 

Code :
  1. <object>
  2. <nickname>Planet</nickname>
  3. <archetype>truc.mesh</archetype>
  4. <pos>0, 0, 0</pos>
  5. </object>


Avouez que c'est plus lourd et moins plaisant. Sûrement que c''est plus simple à parser, mais ca se raproche plus du langage machine ou du langage du compilateur que d'un langage facile à lire.
 
Pour ce qui est de mon code, c'est sûr qu'il est atypique, je l'ai fait à partir de rien. Pas de risque de retrouver une mise en forme ou une fonction bien connue (à part getLine())
 
Le else if ( m == "[Object]" ) est juste là pour le débug. Normalement si tout se passe bien (si le parser fonctionne correctement ;)) cette condition ne se rencontre jamais, grâce au while (lineParsed != lineNbr). C'est juste qu'avant j'avais quelque problème, des lignes qui étaient sautées à cause d'un mauvais recalage du curseur (un bit par ligne en trop).
 
Enfin, quand tu dit _darkalt3_ que si on rajoute une section le code la parse pas, et bien je suppose que c'est vrai, à moins que je ne l'ai définit dans le code qu'un tel type de section existait. Donc c'est pas un problème puisque ces fichiers inis sont sensé n'être composé que des sections X et Y, si l'utilisateur en rajoute une Z inatendue elle ne sera pas parsée, tant mieu :-)
 
PS : je suis pas encore un pro en C++ j'ai commencé il y a 1 an et je le travaille pas beaucoup (je suis encore jeune  :ange: ) donc il y a encore pas mal de notions qui m'échappent, y compris les maps. Donc j'ai de la recherche doc. à faire ;)

Message cité 1 fois
Message édité par Kaalith le 05-04-2006 à 14:21:43

---------------
- Kal
Reply

Marsh Posté le 05-04-2006 à 14:19:18    

Ca veut juste dire que ton programme évoluera difficilement si tes besoins changent, ce qui n'est pas très bon pour un programme objet.
 
C'est très rapide de prendre des mauvais reflexe, change pendant que tu es en apprentissage ! Tu verras alors entre autre qu'INI c'est de la bouillie par rapport à un beau fichier XML et ses beaux nodes.

Reply

Marsh Posté le 05-04-2006 à 14:27:03    

Kaalith a écrit :

Groumph ^^ Pourquoi le fait que l'extension soit INI change quoi que ce soit à la portabilité ;) ? L'essentiel est la façon de le parser.
Pour info, le jeu Freelancer utilise ce type de fichier INI pour quasiment tout (sauf les ressources son textures et models évidement). Il fonctionne sur le même principe (sections de même nom) et fonctionne sur mac :) Donc c'est portable le INI!
Mais je concède que c'est pas du INI généralisé, qu'on utilise dans les fichier de config. C'est du ini Orienté Objet :-)
 
Et désolé je ne veux pas utiliser la bouille XML :P C'est du chinoi, et vraiment pas sympa à éditer. Les inis sont super pour permettre à l'utilisateur de modifier le contenu du programme facilement et sans se prendre (trop) la tête.
 

Code :
  1. <object>
  2. <nickname>Planet</nickname>
  3. <archetype>truc.mesh</archetype>
  4. <pos>0, 0, 0</pos>
  5. </object>


Avouez que c'est plus lourd et moins plaisant. Sûrement que c''est plus simple à parser, mais ca se raproche plus du langage machine ou du langage du compilateur que d'un langage facile à lire.
 
Pour ce qui est de mon code, c'est sûr qu'il est atypique, je l'ai fait à partir de rien. Pas de risque de retrouver une mise en forme ou une fonction bien connue (à part getLine())
 
Le else if ( m == "[Object]" ) est juste là pour le débug. Normalement si tout se passe bien (si le parser fonctionne correctement ;)) cette condition ne se rencontre jamais, grâce au while (lineParsed != lineNbr). C'est juste qu'avant j'avais quelque problème, des lignes qui étaient sautées à cause d'un mauvais recalage du curseur (un bit par ligne en trop).
 
Enfin, quand tu dit _darkalt3_ que si on rajoute une section le code la parse pas, et bien je suppose que c'est vrai, à moins que je ne l'ai définit dans le code qu'un tel type de section existait. Donc c'est pas un problème puisque ces fichiers inis sont sensé n'être composé que des sections X et Y, si l'utilisateur en rajoute une Z inatendue elle ne sera pas parsée, tant mieu :-)
 
PS : je suis pas encore un pro en C++ j'ai commencé il y a 1 an et je le travaille pas beaucoup (je suis encore jeune  :ange: ) donc il y a encore pas mal de notions qui m'échappent, y compris les maps. Donc j'ai de la recherche doc. à faire ;)


 
le XML permet une chose que ton INI est incapable de faire: la notion d'arborscence.
 
exemple simple tiré de mon prjet perso::
 
<StellarCorpses>
 
 <! - Terre - >
 <Planet ray=60000 position=0,0,-300000>
  <RenderParameters include="planets/earth.xml" />    
  <Spin axis=0,1,0 speed=0.03 />
  <! Orbit axis=0,1,0 speed=0.03 />
   
  <StellarCorpses>  
           
                        <! - Lune - >
   <Planet ray=10000 position=-200000,0,0>
    <RenderParameters include="planets/moon.xml" />
    <Spin axis=0,1,0 speed=0.03 />
    <Orbit axis=0,1,0 speed=0.03 />  
   </Planet>  
 
  </StellarCorpses>
   
 </Planet>
 
 <! - Mars ->
 <! ray=3350000 position=227840000000,0,0>
 <Planet ray=50000 position=-20000,0,100000>
  <RenderParameters include="planets/mars.xml" />
  <Spin axis=0,1,0 speed=0.03 angle=3 />
  <!Orbit axis=0,1,0 speed=0.03 />
 </Planet>
 
</StellarCorpses>
 
va exprimer ça avec ton [ini]

Reply

Marsh Posté le 05-04-2006 à 14:29:29    

Bah, niveau évolution... Que j'explique mon projet : je compte enfait créer un prog qui puisse lire les données du jeu Freelancer (dont je parle un peu plus haut). Je sais tout de suite à quoi m'en tenir ;) !
Mais c'est clair qu'avec une fonction ca serait vraiment plus pratique pour la suite, et surtout moins lourd. Seulement je suis pas encore au point avec les fonctions. Je ne sais pas comment lui transmettre les paramètres du code dans lequel elle est exécutée (exemple ici : le mSceneMgr qui est le centre de la création de la scene 3d. Il y en a un seul et unique en même temps seulement je ne vais pas le rajouter comme argument de la fonction, vu qu'il n'y en a qu'un ca ferai tâche (mais le nom peut changer d'un code à l'autre)).
Je pense que je pourrait faire une fonction pas trop compliquée qui dirait "lire chaque section [ Xyz ] puis récupérer les paramètres" seulement dans quel type de conteneur je peux stoquer les données retournées par ce type de fonction ?
 
Edit : pour répondre à bjone (trop rapide ^^)
Il suffirait de rajouter un paramètre "attachTo =" ou "parent =" :p

Message cité 1 fois
Message édité par Kaalith le 05-04-2006 à 14:33:47

---------------
- Kal
Reply

Marsh Posté le 05-04-2006 à 14:29:54    

en Xml, tu peux faire (je pense, enfin je me gênes pas pour le faire avec TinyXmeul:
 
 <object>
         <nickname>Planet</nickname>
         <archetype>truc.mesh</archetype>
         <pos>0, 0, 0</pos>
 </object>  
 
ou:
 
<object  
    nickname=Planet
    achetype=truc.mesh
    pos=0,0,0
/>
 
ce qui est pas vraiment différent de l'Ini, mais comme j'ai dit, c'est plus facile d'encapsuler une arborescence.

Reply

Marsh Posté le 05-04-2006 à 14:32:56    

Kaalith a écrit :

Bah, niveau évolution... Que j'explique mon projet : je compte enfait créer un prog qui puisse lire les données du jeu Freelancer (dont je parle un peu plus haut). Je sais tout de suite à quoi m'en tenir ;) !
Mais c'est clair qu'avec une fonction ca serait vraiment plus pratique pour la suite, et surtout moins lourd. Seulement je suis pas encore au point avec les fonctions. Je ne sais pas comment lui transmettre les paramètres du code dans lequel elle est exécutée (exemple ici : le mSceneMgr qui est le centre de la création de la scene 3d. Il y en a un seul et unique en même temps seulement je ne vais pas le rajouter comme argument de la fonction, vu qu'il n'y en a qu'un ca ferai tâche (mais le nom peut changer d'un code à l'autre)).
Je pense que je pourrait faire une fonction pas trop compliquée qui dirait "lire chaque section [ Xyz ] puis récupérer les paramètres" seulement dans quel type de conteneur je peux stoquer les données retournées par ce type de fonction ?


 
ha dommage héliane....
 
sinon un truc que j'utilise perso pour cacher les .ini des apps pourries au taf:
 

Code :
  1. #pragma once
  2. #include <hash_map>
  3. #include <string>
  4. class ProfileSectionHashCache : public stdext::hash_map<std::string, std::string>
  5. {
  6. public:
  7. std::string operator [] (const std::string & ) const;
  8. bool Load( const std::string &File,  const std::string &Section );
  9. ProfileSectionHashCache() {};
  10. ProfileSectionHashCache( const std::string &File,  const std::string &Section );
  11. };


 

Code :
  1. #include "ProfileHashCache.h"
  2. #include <boost/algorithm/string.hpp>
  3. #include <fstream>
  4. using namespace std;
  5. using namespace boost::algorithm;
  6. bool parse_header( const string &line, string &header )
  7. {
  8. if( line.length() <= 2 )
  9.  return false;
  10. if( line[0]!='[' || line[line.length()-1]!=']' )
  11.  return false;
  12. header=line.substr(1, line.length()-2 );
  13. return true;
  14. }
  15. bool parse_entry( const string &line, string &key, string &value )
  16. {
  17. size_t pos=line.find('=');
  18. if( pos == string::npos )
  19.  return false;
  20. key=line.substr(0,pos);
  21. value=line.substr(pos+1);
  22. return true;
  23. }
  24. string prepare_key(const string &Key)
  25. {
  26. string Str(Key);
  27. trim(Str);
  28. to_upper(Str);
  29. return Str;
  30. }
  31. string ProfileSectionHashCache::operator [] (const string &Key) const
  32. {
  33. const_iterator iter=find( prepare_key(Key) );
  34. if( iter != end() )
  35.  return iter->second;
  36. else
  37.  return string(); // string vide à tester avec string.empty()  
  38. }
  39. bool ProfileSectionHashCache::Load( const std::string &File,  const std::string &Section )
  40. {
  41. ifstream ifs( File.c_str() );
  42. if( !ifs )
  43.  return false;
  44. string SectionKey=prepare_key(Section);
  45. bool SectionFound=false;
  46. while( ifs )
  47. {
  48.  string line;
  49.  getline( ifs, line );
  50.  trim(line);
  51.  string key;
  52.  if( parse_header( line, key) )
  53.  {
  54.   if( SectionFound )   // encore une nouvelle section trouvée => fin de la section demandée
  55.    break;
  56.   if( prepare_key(key) == SectionKey ) // vérification si c'est bien la section demandée
  57.   {
  58.    SectionFound=true;
  59.    continue;
  60.   }
  61.  }
  62.  string value;
  63.  if( SectionFound && parse_entry(line,key,value) ) // on est dans la section demandée, on parse la ligne pour obtenir la clé/valeur    
  64.   insert(  pair<string,string>(prepare_key(key), value) );  // et on la stocke avec la clé formatée
  65. }
  66. return true;
  67. }
  68. ProfileSectionHashCache::ProfileSectionHashCache( const std::string &File,  const std::string &Section )
  69. {
  70. Load( File, Section );
  71. }


 
c'est pas exactement ce qu'il te faut, mais au moins ça te permettera d'avoir ptet des idées, pour restructurer ton truc si tu le veut.
 
genre avec .ini style:
 
[Dirs]
a=c:\454dsfsdfs
b=d:\4464
 
[params]
ui=zog
plouf=floc
 
avec du code style:
ProfileSectionHashCache Dirs("biniou.ini","dirs" );  
ProfileSectionHashCache Params("biniou.ini","params" );  
 
std::string ui, plouf;
std::string DirA,DirB;
 
ui=Params["ui"];
plouf=Params["plouf"];
 
DirA=Dirs["a"];
DirB=Dirs["b"];


Message édité par bjone le 05-04-2006 à 14:38:44
Reply

Marsh Posté le 05-04-2006 à 14:41:01    

tiens d'ailleurs il doit y avoir un bug, faudra que je zieutes pourquoi ça marche :D
nan en fait c'est bon, je me suis fourvoyé.


Message édité par bjone le 05-04-2006 à 14:43:04
Reply

Marsh Posté le 05-04-2006 à 14:56:34    

^^ Bon ca m'a prit un peu de temps pour rentrer de dedans mais c'est bon j'ai pigé le principe :D !
Je suppose qu'utilisé brut comme il est il ne permet pas d'avoir plusieurs section de même nom, mais ca doit pouvoir se faire avec un peu de bidouille.
Donc enfait je pourrais stoquer toutes les valeurs dans un StringVector (je sais pas si c'est standard mais en tout cas le moteur graphique que j'utilise me permet de les utiliser), comme une chaine de caractères en somme, une chaine de string ^^ Ensuite je converti les valeurs numériques en int / real selon la situation... Y a moyen :)
Je vais essayer ça.
Merci pour le code :P
 
Ps : je connaissais pas la fonction trim, bien pratique.
Pour info j'utilise SDL_config comme parser actuellement (il fonctionne mais ne prend pas les sections de même noms, juste la première).
 
EDIT : heu parcontre je vois pas l'utilité d'inclure boost. Y a une fonction de là bas que j'ai pas repéré ?
ReEDIT : hé ben trim justement ^^ et to_upper, mais c'est pas grave je les ai déja avec OGRE.


Message édité par Kaalith le 05-04-2006 à 15:25:52

---------------
- Kal
Reply

Marsh Posté le 05-04-2006 à 15:53:57    

trim  
to_upper
sur les std::string
 
sont des algos de boost ;)
 
edit: exact j'avais pas vu que tu l'avais vu :D


Message édité par bjone le 05-04-2006 à 15:54:55
Reply

Marsh Posté le 05-04-2006 à 15:56:06    

Vi j'ai vu ça ;) (et pas vu que tu avais vu que j'avais vu... gni)
Par contre je sais pas si c'est voulu mais dans ton code la valeur retrounée n'est pas formatée : il reste des espaces avant.
Il faut lui appliquer le même type de fonction que prepare_value, sans le to_upper.
Enfin c'est ce que j'ai fait :)
 
Edit : heu par contre j'ai vu nule part que tu fermais le flot. Je doit le faire manuellement non ? Une foi que j'ai lu toutes mes valeurs.

Message cité 1 fois
Message édité par Kaalith le 05-04-2006 à 22:07:39

---------------
- Kal
Reply

Marsh Posté le 05-04-2006 à 16:17:56    

pour l'... operator[] ?
 
parceque dans nos super fichier de données, il y a des infos qui servent a afficher une arborescence, et les espaces comptent, genre:
 
[plouf]
yop1=haha racine
yop2=                    huhu element
yop3=                           huhu zog
yop4=                    hihi element 2
 
enfin :lol: quoi
 

Reply

Marsh Posté le 05-04-2006 à 16:21:36    

Kaalith a écrit :

Vi j'ai vu ça ;)
Par contre je sais pas si c'est voulu mais dans ton code la valeur retrounée n'est pas formatée : il reste des espaces avant.
Il faut lui appliquer le même type de fonction que prepare_value, sans le to_upper.
Enfin c'est ce que j'ai fait :)
 
Edit : heu par contre j'ai vu nule part que tu fermais le flot. Je doit le faire manuellement non ? Une foi que j'ai lu toutes mes valeurs.


 
 
la lecture et la récupération des couples clé=valeur pour la section désiré
s'arrête soit à la fin du fichier :D
soi lorsque une nouvelle [section] est rencontré, avec le:
 
            if( SectionFound )            // encore une nouvelle section trouvée => fin de la section demandée
                break;
 
qui break le while() si SectionFound est positionné pour indiquer que la bonne section a été trouvée.
 
mais vu que pour ton cas d'utilisation, c'est différent (ce serait un vector< hash_map<string,string> >, vector pour la collection de  [Object]), bin il faut que tu revoyes toute la boucle générale.


Message édité par bjone le 05-04-2006 à 16:22:32
Reply

Marsh Posté le 05-04-2006 à 22:02:42    

Ouai oki donc j'ai pas besoin de ce type d'arborescence ;)
Mais je savais pas que "breaker" un while rendait forcément sa condition en false (enfin dans ce cas).
Sinon c'est bon j'ai réussi à l'intégrer dans mon code sans soucis, bien qu'il ne parse qu'une seule section de même nom.
Je suis en train de bosser là dessus ;)
 
Je vais rajouter un paramètre suplémentaire aux fonctions Load et ProfileSectionHashCache (que j'ai renommé en LoadSection, plus simple à écrire :)), un int : sectionNbr. Je ferais ensuite un while qui scannera chaque section par son numéro (faudra aussi que je rejoute une fonction qui lit le nombre de groupe du même nom ^^).
Ah ca me passe par la tête : dis le moi si ca te dérange que j'utilise le code de ta boite :P


Message édité par Kaalith le 05-04-2006 à 22:11:26

---------------
- Kal
Reply

Marsh Posté le 05-04-2006 à 22:52:46    

tu fais comme tu le sens, du moment que t'arrives a avoir un truc qui marche.
 
mais si c'est pour loader l'ini une seule fois pour toute, pas non plus besoin de vector<> ou hash_map, il te suffit de charger ligne par ligne, parser et pousser dans un vector<> tes objets.
 
moi j'avais orienté le truc sous forme de cache avec un hash_map parceque ce que les apps que je retouchais ne conservaient rien par elles-mêmes et se reposaient entièrement sur les ProfileString machin de l'api Win32 et que le truc mettait 3 heures a réagir quand tu cliquais sur un bouton....


Message édité par bjone le 05-04-2006 à 22:53:07
Reply

Marsh Posté le 05-04-2006 à 22:55:28    

par contre aussi, je sais pas si ça été dit, mais ton:
 
while(!Entities.eof())
 
est sujet a erreur, .eof() ne teste pas tous les cas de figures de fin/coupure de flux, pour avoir un truc sain, il faut écrire:
 
while( Entities )
 
tout simplement.

Reply

Marsh Posté le 05-04-2006 à 22:56:49    

:)
Non enfait mes fichiers vont sans doute être réaccèdés pendant le fonctionnement de l'app.
Je pense avoir presque réussi, mais j'ai un problème, mon int sectionNbr qui ne passe pas dans la fonction ^^ (reste à sa valeur pas défaut, 0)
Je suis un peu trop naze pour fixer ça ce soir, je verrais demain ;) !
Bonne nuit.
 
Edit : oki j'essayerai ce changement avec mon ancien code voir si ca règle le problème ;) Merci beaucoup pour ton soutien ca fait vraiment plaisir.


Message édité par Kaalith le 05-04-2006 à 22:57:52

---------------
- Kal
Reply

Marsh Posté le 06-04-2006 à 21:17:51    

Bon j'ai un petit soucis, une chose qui m'échape :
Mon code devrait fonctionner correctement (du moins je pense)  seulement il y a un problème avec la variable qui compte le nombre de   section correspondant au nom cherché (appelée ici zogzog, je voulais être sûr qu'il y ait pas un mélange quelconque avec une autre variable  ^^). En effet, celle si est donc égale à 0 tant que la condition ( prepare_key(key) == SectionKey ) n'est pas vérifiée :

Code :
  1. if( prepare_key(key) == SectionKey )
  2. {
  3. [LogMessage 1]
  4. zogzog += 1;
  5. [LogMessage 2]
  6. }


(j'ai simplifié l'affichage du débug dans le log pour clarifier le code)
Puis dès qu'une section dont le nom correspond à celui cherché, je rajoute 1 à cette variable.
Puis le programme compare avec le numéro de la section passé dans la fonction. Si la valeur est la même alors on revient au debut de la boucle pour lire les valeurs.

Code :
  1. [LogMessage 3]
  2. if (zogzog = sectionNbr)
  3. {
  4. [LogMessage 4]
  5. SectionFound = true;
  6. continue;
  7. }


Puis on s'arrète quand la ligne suivante est une nouvelle section.

Code :
  1. if (SectionFound)
  2. break;


 
C'est pas bien compliqué :)
Seulement dans mon fichier j'ai inséré une section autre (différente de [Object]), dans le debug on peut lire, dès que la première section (enfait la fausse) est rencontrée :
(1 et 2 n'apparaissent pas car la section n'a pas le nom voulu)
Debug 3 : zogzog = 0
Debug 4 : zogzog = 2 !!
 
Si on rassemble toute ma boucle while ca donne :

Code :
  1. while( ifs )
  2. {
  3. string line;
  4. getline( ifs, line );       
  5. while (StringUtil::startsWith(line," " ))
  6.  StringUtil::trim(line,true,false);
  7. while (StringUtil::endsWith(line," " ))
  8.  StringUtil::trim(line,false,true);
  9. string key;
  10. if( parse_header(line, key) )
  11. {
  12.  if (SectionFound)
  13.   break;
  14.  if( prepare_key(key) == SectionKey )
  15.  {
  16.   [LogMessage 1]
  17.   zogzog += 1;
  18.   [LogMessage 2]
  19.  }
  20.  [LogMessage 3]
  21.  if (zogzog = sectionNbr)
  22.  {
  23.   [LogMessage 4]
  24.   SectionFound = true;
  25.   continue;
  26.  }
  27. }
  28. if( SectionFound && parse_entry(line,key,value) )           
  29.  insert(  pair<string,string>(prepare_key(key), prepare_value(value)) );
  30. }


 
J'aimerais qu'on m'explique comme ma variable peut augmenter de 2 sans que rien ne lui soit appliqué directement ^^
On dirais que le programme modifie la variable pour qu'elle corresponde à la valeur qu'on veut qu'elle ait...


Message édité par Kaalith le 06-04-2006 à 21:24:22

---------------
- Kal
Reply

Marsh Posté le 06-04-2006 à 21:30:23    

je crois que tu devrais pas chercher a respecter mon organisation du while.

Reply

Marsh Posté le 06-04-2006 à 21:35:18    

Elle me plait bien pourtant :)
Enfin je ne lui trouve pas de contrainte quand à ce que je veux en faire. Pas toi ?


---------------
- Kal
Reply

Marsh Posté le 06-04-2006 à 21:41:36    

ou alors, on va se la jouer autrement:
 
class Section
{
public:
   std::string Name;
   stdext::hash_map<std::string, std::string> Entries;
};
 
class SectionCollection : public std::vector<Section>
{
public:
   bool Load( const std::string &file );
};
 
et tu me fais le Load() qui lit pousse les Sections dans le vector<> quand il les rencontre, et qui renseigne les Entries de la dernière Section poussée...


Message édité par bjone le 06-04-2006 à 21:47:55
Reply

Marsh Posté le 06-04-2006 à 21:43:39    

ensuite une fois que ta collection de Section est terminée, tu la balayes et tu instancies tes objets, que tu renseignes avec les paramètres lu des Entries de la Section de l'objet.

Reply

Marsh Posté le 06-04-2006 à 21:45:22    

le problème est que lorsque l'on mélange trop le parsage du document et l'instanciation des objets de l'application, on se fout facilement les pieds dans la table....
 
donc il vaux mieux passer par un objet document, que tu renseignes, et ensuite que tu manipules...


Message édité par bjone le 06-04-2006 à 21:45:46
Reply

Marsh Posté le 06-04-2006 à 22:11:35    

Oki je garde ça en tête :)
Mais j'ai enfin réussi :
Le problème était plustôt stupide :
if (zogzog = sectionNbr)
Au lieu de :
if (zogzog == sectionNbr)
...
 
Voilà ce qui arrive quand on est pas assez instruit en programmation ^^


---------------
- Kal
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed