OVH Cloud OVH Cloud

interpréteur xml

14 réponses
Avatar
ladycompaq
Bonjour à tous,

j'ai comme devoir de réaliser un interpréteur XML mais je n'ai aucune
idée comment m'y prendre.... je vous résume un peu le problème...

ont considère que le document XML est bien construit.

l'interpréteur doit reconnaitre la structure du document XML (10 pts)
il doit reconnaitres les noeuds de type textuel (2pts)
il doit reconnaitre les éléments vide ex: <HR/> (1 pts)
il doit reconnaitre les attributs (2 pts)


Ont doit lire le fichier XML en entré et générer un rapport en sortie.
On doit utiliser la récursivité et la POO pour gérer le fichier en
mémoire.
Le fichier en sortie doit etre sous le format suivant:

<A>
<B id="test">
<C>texte de C</C>
<D type="alpha"/>
<B/>
<A/>

Ont doit fournir le rapport suivant:

Le noeud <A> contient
Le noeud <B> contient
L'attribut « id » qui a la valeur «test»
Le noeud <C> contient
Le texte «texte de C»
Le noeud <D> contient
L'attribut «type» qui a la valeur «alpha»


Je vous demande votre aide car je n'y comprend absolument rien....
Alors que les meilleurs se mettent à leur clavier pour me venir en
aide!!!

Merci d'avance
Johanne

10 réponses

1 2
Avatar
Michel Michaud
Dans le ,
Bonjour à tous,

j'ai comme devoir de réaliser un interpréteur XML mais je n'ai
aucune idée comment m'y prendre.... je vous résume un peu le
problème...
[...]

Je vous demande votre aide car je n'y comprend absolument
rien....


À quoi exactement ? À XML ? Ici c'est un groupe sur C++.

Alors que les meilleurs se mettent à leur clavier pour me venir
en aide!!!


Je veux bien essayer de me mettre au clavier, mais que dois-je
essayer de t'expliquer exactement ? L'énoncé du problème ?

(Ici on ne fait pas les devoirs pour les autres, on aide les
autres à comprendre comment les faire eux-mêmes, ce qui leur
sera beaucoup plus utile. Il va falloir que tu poses des
questions plus précises et surtout qui ont un rapport avec C++.)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Twxs
Country wrote:
Bonjour à tous,


Ont doit lire le fichier XML en entré et générer un rapport en sortie.
On doit utiliser la récursivité et la POO pour gérer le fichier en
mémoire.
Le fichier en sortie doit etre sous le format suivant:

<A>
<B id="test">
<C>texte de C</C>
<D type="alpha"/>
<B/>
<A/>

Ont doit fournir le rapport suivant:

Le noeud <A> contient
Le noeud <B> contient
L'attribut « id » qui a la valeur «test»
Le noeud <C> contient
Le texte «texte de C»
Le noeud <D> contient
L'attribut «type» qui a la valeur «alpha»


Je vous demande votre aide car je n'y comprend absolument rien....
ca depend evidemment de ton nivau, mais un parser xml n'est au final pas

tres difficile a implementer.
les etapes classiques sont :
- faire un lexer qui s'occupe de decouper en token ton fichier
d'entree(google)
- parser en se servant des tokens (pour le xml comme la syntaxe est
simple, le lexer n'est pas necessaire mais je te conseille qd meme d'en
utiliser un)

pour la recursivité, ca saute au yeux enfin pour moi. un tag contiend
d'autre tag.
si tu as une classe Tag avec une methode parse tu auras forcement un
truc du style :
Tag::parse(...){
...
subTag->parse....
...
}
d'une maniere generale c la stucture d'arbre derriere le xml qui
implique ca.

utiliser la POO s'y prete bien, une classe Tag, une Attrib
un attribut contiend un nom et une valeur par exemple.

pour le rapport de sortie, tu verras que lorsque tu auras parse ton
fichier et eu ton xml en memoire, cette partie ne devrait pas depasser 6
lignes de code

Alors que les meilleurs se mettent à leur clavier pour me venir en
aide!!!
faut pas esperer recuperer les sources de ton projet ici ;-)


Je tente juste de te donner des piste pour commencer (j'en ai pas trop
dit, hein?). Je pense (j'espere...)que c'est ce que tu venais chercher
ici. D'une maniere generale force toi a t'y mettre toi meme, c'est dur
au debut, mais ce n'est pas du temps perdu. Je pense qu'on restera a ta
disposition sur d'eventuelle pb lié à l'implem, comme l'utilisation des
stl (mon dernier parser xml en abuse) plutot que de conception (enfin je
me comprend...)

au fait, c un projet de quel nivau (licence, ...)?


--Twxs

Avatar
kanze
Twxs wrote in message
news:<41551a2a$0$23958$...
Country wrote:
Ont doit lire le fichier XML en entré et générer un rapport en
sortie. On doit utiliser la récursivité et la POO pour gérer le
fichier en mémoire.

Le fichier en sortie doit etre sous le format suivant:

<A>
<B id="test">
<C>texte de C</C>
<D type="alpha"/>
<B/>
<A/>

Ont doit fournir le rapport suivant:

Le noeud <A> contient
Le noeud <B> contient
L'attribut « id » qui a la valeur «test»
Le noeud <C> contient
Le texte «texte de C»
Le noeud <D> contient
L'attribut «type» qui a la valeur «alpha»

Je vous demande votre aide car je n'y comprend absolument rien....


ca depend evidemment de ton nivau, mais un parser xml n'est au final
pas tres difficile a implementer.


Ça dépend.

les etapes classiques sont :
- faire un lexer qui s'occupe de decouper en token ton fichier
d'entree(google)
- parser en se servant des tokens (pour le xml comme la syntaxe est
simple, le lexer n'est pas necessaire mais je te conseille qd meme
d'en utiliser un)


Le problème, c'est que le comportement du lexer doit dépendre au moins
en partie de l'état du parser. En dehors des tags, par exemple, les
blancs sont significatifs -- en dehors d'un tag, le lexer doit avaler
tout jusqu'au prochain < ou &. En fait, je crois qu'on ne lexe que les
tags.

Je crois que si j'avais à résoudre le problème, et que je n'avais pas
besoin de supporter des compilateurs trop anciens, je me servirai de
boost::regex pour extraire les tags et le texte entre les tags. À
l'intérieur des tags, en revanche, il se peut qu'un lexer se justifie,
bien que je crois que boost::regex pourrait servir là aussi. (Note bien
que l'expression régulière nécessaires ne sont pas triviales, au moins
si on tient à permettre les < et al dans des chaînes attributes.) Sinon,
lex permet des états, de façon qu'une fois qu'on a reconnu le début d'un
tag, on peut changer d'état.

pour la recursivité, ca saute au yeux enfin pour moi. un tag contiend
d'autre tag.


Un noeud contient d'autres noeuds, tu veux dire. Ce qui rend la
grammaire si simple, c'est précisement qu'un tag ne peut contenir que
son identificateur et des paires attribute/valeur. Ce qui permet
justement au lexer de changer d'état tout seul, sans intervention du
parser.

si tu as une classe Tag avec une methode parse tu auras forcement un
truc du style :
Tag::parse(...){
...
subTag->parse....
...
}
d'une maniere generale c la stucture d'arbre derriere le xml qui
implique ca.

utiliser la POO s'y prete bien, une classe Tag, une Attrib un attribut
contiend un nom et une valeur par exemple.


C'est même normalisé, voir
http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/. Pour ce qu'il a à
faire, Node, Element, Attribute et Text doivent suffir. (Node, c'est la
classe de base des autres.) Les fonctions clé pour une implémentation
C++ seraient childNodes() et attributes(). (L'interface standard est
conçue pour supporter le chaînage invasif, mais je crois que pour une
implémentation primitive en C++, je me contenterai d'un std::list
renvoyé de childNodes(), et je laisserais tomber les fonctions sur les
siblings.)

pour le rapport de sortie, tu verras que lorsque tu auras parse ton
fichier et eu ton xml en memoire, cette partie ne devrait pas depasser
6 lignes de code

Alors que les meilleurs se mettent à leur clavier pour me venir en
aide!!!


faut pas esperer recuperer les sources de ton projet ici ;-)

Je tente juste de te donner des piste pour commencer (j'en ai pas trop
dit, hein?).


La piste, c'est la récursion. Il le lui faut et pour parser (descente
récursive, à moins qu'il se sert de yacc ou de quelque chose de
semblable), et pour l'affichage.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Twxs

C'est même normalisé, voir
http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/. Pour ce qu'il a à
faire, Node, Element, Attribute et Text doivent suffir. (Node, c'est la
classe de base des autres.) Les fonctions clé pour une implémentation
C++ seraient childNodes() et attributes(). (L'interface standard est
conçue pour supporter le chaînage invasif,


chainage invasif, qu'est-ce donc?

mais je crois que pour une implémentation primitive en C++, je me contenterai d'un std::list
renvoyé de childNodes(), et je laisserais tomber les fonctions sur les
siblings.)


moi j'utilise un std::multimap avec en clef le nom du sous tag et ca se
passe plutot bien ;-)

Twxs

Avatar
kanze
Twxs wrote in message
news:<cj8omv$hbs$...

C'est même normalisé, voir
http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/. Pour ce qu'il a
à faire, Node, Element, Attribute et Text doivent suffir. (Node,
c'est la classe de base des autres.) Les fonctions clé pour une
implémentation C++ seraient childNodes() et attributes().
(L'interface standard est conçue pour supporter le chaînage invasif,


chainage invasif, qu'est-ce donc?


C'est quand l'objet connaît ses frères, en quelque sort. C'est quand les
liens de chaînage font partie integrante de l'objet.

Ça sert souvent avec les objets d'entité, qui ne supporte pas la copie,
et qui se trouve dans exactement une chaîne à la fois. Une chaînage
double invasif a l'avantage que l'objet peut s'ôter de la chaîne dans
son destructeur, sans savoir dans quelle chaîne il est.

J'avoue que je ne crois pas que ce soit particulièrement nécessaire,
voir même intéressant, dans le cas du XML. Sauf si on tient à
implémenter l'interface « standard », parce que je vois mal comment
implémenter getNextSibling() et getPreviousSibling() sans chaînage
invasif. (Mais c'est une raisonement circulaire. L'intérêt principal de
getNextSibling() et getPreviousSibling(), c'est de supporter l'itération
sur un chaînage invasif.)

mais je crois que pour une implémentation primitive en C++, je me
contenterai d'un std::list renvoyé de childNodes(), et je laisserais
tomber les fonctions sur les siblings.)


moi j'utilise un std::multimap avec en clef le nom du sous tag et ca
se passe plutot bien ;-)


Et comment fais-tu quand les sous-tag comportent des éléments avec le
même sous-tag ?

En général, la structure est récursive. J'aurais une tendance à
implémenter au moins Node::getAttributes() et Node::getChildNodes() (et
probablement Node::getParent()) de l'interface standard. Mais rien
n'empèche d'avoir des map à côté, afin de pouvoir rétrouver certains
éléments plus rapidement. Et j'avoue que pour beaucoup d'applications,
les types de vertice Attribute, Element et Text suffirait largement.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
writes:

Ça sert souvent avec les objets d'entité, qui ne supporte pas la
copie, et qui se trouve dans exactement une chaîne à la fois. Une
chaînage double invasif a l'avantage que l'objet peut s'ôter de la
chaîne dans son destructeur, sans savoir dans quelle chaîne il est.


Comment peux-tu programmer le destructeur si tu ne connais pas la
chaine ?

Avec le lien vers l'arriere qui est un pointeur vers pointeur ? Mais
alors je ne vois pas comment implementer le getPreviousSibling() dont
tu parles apres (un cast va foirer si on applique getPreviousSibling
au premier de la liste).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Twxs
wrote:

Et comment fais-tu quand les sous-tag comportent des éléments avec le
même sous-tag ?

En général, la structure est récursive. J'aurais une tendance à
implémenter au moins Node::getAttributes() et Node::getChildNodes() (et
probablement Node::getParent()) de l'interface standard. Mais rien
n'empèche d'avoir des map à côté, afin de pouvoir rétrouver certains
éléments plus rapidement. Et j'avoue que pour beaucoup d'applications,
les types de vertice Attribute, Element et Text suffirait largement.


voila ce que ca donne en version light
ca se prete assez bien a la manipulation
comme recuperer les sous tags portant un certain nom, etc

struct Attrib{
pair<string, string> _value;
};

struct Tag{
string _name;
multimap<string, Tag*> _children;
map<string, Attrib*> _attributes
};

c bien entendu une structure recursive, donc l'un dans l'autre, je pense
qu'on parle de la meme chose ;-)

Avatar
kanze
Jean-Marc Bourguet wrote in message
news:...
writes:

Ça sert souvent avec les objets d'entité, qui ne supporte pas la
copie, et qui se trouve dans exactement une chaîne à la fois. Une
chaînage double invasif a l'avantage que l'objet peut s'ôter de la
chaîne dans son destructeur, sans savoir dans quelle chaîne il est.


Comment peux-tu programmer le destructeur si tu ne connais pas la
chaine ?


template< typename T >
class ListIn // ListIn<T> doit être classe de base de T
{
ListIn* next ;
ListIn* prec ;

public:
void removeFromAllLists()
{
next->prec = prec ;
prec->next = next ;
next = prec = this ;
}

virtual ~ListIn()
{
removeFromAllLists() ;
}
} ;

Quelque part, évidemment, il y a un ListIn<T> qui sert de tête de liste
(et qui n'est pas une classe de base). Pour lui, on a aussi des
fonctions comme la suivante :

bool
ListIn<T>::isEmpty() const
{
return next == this ;
}

L'itérateur contient un ListIn<T>*, évidemment. Celui de begin() est
initialisé avec next, celui de end() avec this.

Avec le lien vers l'arriere qui est un pointeur vers pointeur ? Mais
alors je ne vois pas comment implementer le getPreviousSibling() dont
tu parles apres (un cast va foirer si on applique getPreviousSibling
au premier de la liste).


ListIn<T>*
ListIn<T>::getPrecedant() const
{
return prec ;
}

Node*
Node::getPreviousSibling() const
{
return dynamic_cast< Node* >( ListIn<Node>::getPrevious() ) ;
}

On ne connaît pas a priori la tête de la liste, mais on sait le
reconnaître du fait que c'est le seul membre qui n'est pas une classe de
base de T.

Il faut dire que dans ce cas précis, il y a aussi un getParent(), à
travers duquel on pourrait aussi trouver la tête de la liste. Mais ce
n'est pas nécessaire, et ça ne sert pas dans la plupart des cas.

On remarque qu'en fait, ListIn implémente deux interfacees -- une qui
sert quand elle est classe de base d'un élément, et l'autre quand elle
est racine de la liste. Donc, d'une part, on a getPrecedant(),
ci-dessus, qui sert par exemple dans les itérateurs, et de l'autre, on a
des fonctions comme :

ListIn<T>*
ListIn<T>::getFirst() const
{
return next == this ? null : next ;
}

ListIn<T>::iterator
ListIn<T>::begin()
{
return iterator( next ) ;
}

Qui serviraient logiquement sur la tête de liste.

Ça suppose une certaine discipline dans l'utilisation, mais dans la
pratique, ça ne m'a jamais posé de problème.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Twxs wrote in message
news:<cjbddl$lbt$...
wrote:

Et comment fais-tu quand les sous-tag comportent des éléments avec
le même sous-tag ?

En général, la structure est récursive. J'aurais une tendance à
implémenter au moins Node::getAttributes() et Node::getChildNodes()
(et probablement Node::getParent()) de l'interface standard. Mais
rien n'empèche d'avoir des map à côté, afin de pouvoir rétrouver
certains éléments plus rapidement. Et j'avoue que pour beaucoup
d'applications, les types de vertice Attribute, Element et Text
suffirait largement.


voila ce que ca donne en version light
ca se prete assez bien a la manipulation
comme recuperer les sous tags portant un certain nom, etc

struct Attrib{
pair<string, string> _value;
};

struct Tag{
string _name;
multimap<string, Tag*> _children;
map<string, Attrib*> _attributes
};

c bien entendu une structure recursive, donc l'un dans l'autre, je
pense qu'on parle de la meme chose ;-)


Pas tout à fait. Ça marche pour les attributes, je crois, mais l'ordre
des vertices fils est significatif. Si j'ai quelque chose du genre :

<body>
<p>
Du texte.
<p>
<table>
<tr>
<td>un</td>
<td>deux</td>
</tr>
</table>
<p>
Encore du texte.
</p>
</body>

c'est important de pouvoir visiter les sous-vertices de <body> dans
l'ordre qu'elles s'y trouvent, c-à-d <p><table><p>. Or, ton multimap va
les renvoyer dans l'ordre <p><p><table> -- je crois que l'ordre des deux
<p> n'est même pas défini.

En super-lèger, on pourrait avoir quelque chose du genre :

struct Node
{
std::list< Node* > descendance ;
std::map< std::string, Attribute* >
attributes ;
} ;

Avec un multimap à côté pour accéder rapidement à certains types
d'éléments, si tu veux. Mais à mon avis, ça ne se justifie que dans les
cas extrèmes. Une déscente récursive n'est pas si chère que ça, et
suffit sans doute dans la plupart des applications. Aussi, j'avoue que
je ne vois pas trop l'intérêt de pouvoir récupérer par exemple tous les
<table> dans un document ; normalement, une table n'a de la
signification qu'en contexte. Et dans les applications autres que HTML,
c'est en général encore plus vrai. Dans les applications que j'ai fait,
le document consistait toujours d'une liste d'éléments avec une
attribute de nom, et avec des sous-éléments qui comportaient
l'information sur l'élément.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Jean-Marc Bourguet
writes:

Jean-Marc Bourguet wrote in message
news:...

Comment peux-tu programmer le destructeur si tu ne connais pas la
chaine ?


[Mixin, Liste circulaire, dynamic_cast]


OK. C'est un bon exemple d'utilisation du RTTI.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


1 2