Parser INI [Résolu] - C++ - Programmation
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.
Marsh Posté le 04-04-2006 à 23:10:11
euh... tu connais les fonctions Win32 WritePrivateProfileString et GetPrivateProfileString ?
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
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)
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
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.
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 |
Code :
|
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.
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...
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 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 :
|
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 ) donc il y a encore pas mal de notions qui m'échappent, y compris les maps. Donc j'ai de la recherche doc. à faire
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.
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.
|
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]
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 ="
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.
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 ! |
ha dommage héliane....
sinon un truc que j'utilise perso pour cacher les .ini des apps pourries au taf:
Code :
|
Code :
|
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"];
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
nan en fait c'est bon, je me suis fourvoyé.
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 !
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
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.
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
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.
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 quoi
Marsh Posté le 05-04-2006 à 16:21:36
Kaalith a écrit : Vi j'ai vu ça |
la lecture et la récupération des couples clé=valeur pour la section désiré
s'arrête soit à la fin du fichier
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.
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
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....
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.
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.
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 :
|
(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 :
|
Puis on s'arrète quand la ligne suivante est une nouvelle section.
Code :
|
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 :
|
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...
Marsh Posté le 06-04-2006 à 21:30:23
je crois que tu devrais pas chercher a respecter mon organisation du while.
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 ?
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...
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.
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...
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 ^^
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 :
[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 ^^
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