OVH Cloud OVH Cloud

Créer un Parser

11 réponses
Avatar
Sébastien Ramage
J'ai un fichier que je souhaiterai parser, =E7a ressemble un peu =E0 du
xml dans l'id=E9e

{
val1 =3D "kljlk";
val2 =3D 133;
val3 =3D {
val12 =3D 23;
val22 =3D "popo";
};

vallist =3D (
{
te =3D 13;
ml =3D 12;
},
{
ml =3D 23;
te =3D 36;
}
);

};

il y a 3 "types de balise",
les simples :
toto =3D qqchose ;
les "enfants":
parent =3D {
enfant1 =3D qqch;
enfant2 =3D qqch;
};
les listes :
liste =3D (
{
toto =3D qqch;
tata =3D qqch;
},
{
titi =3D qqch;
pepe =3D qqch;
momo =3D qqch;
}
);

et biensur comme en xml, tout =E7a peut =EAtre combiner , il peut y avoir
des enfants, d'enfant, d'enfant qui peuvent =EAtre dans des listes etc
etc

pfiou, bref si qqn m'a compris, je veux bien un peu d'aide sur la
fa=E7on de proc=E9der

10 réponses

1 2
Avatar
jean-michel bain-cornu
Bonjour,

Sébastien Ramage wrote:
J'ai un fichier que je souhaiterai parser, ça ressemble un peu à du
xml dans l'idée

{
val1 = "kljlk";
val2 = 133;
val3 = {
val12 = 23;
val22 = "popo";
};

vallist = (
{
te = 13;
ml = 12;
},
{
ml = 23;
te = 36;
}
);

};

il y a 3 "types de balise",
les simples :
toto = qqchose ;
les "enfants":
parent = {
enfant1 = qqch;
enfant2 = qqch;
};
les listes :
liste = (
{
toto = qqch;
tata = qqch;
},
{
titi = qqch;
pepe = qqch;
momo = qqch;
}
);

et biensur comme en xml, tout ça peut être combiner , il peut y avoir
des enfants, d'enfant, d'enfant qui peuvent être dans des listes etc
etc

pfiou, bref si qqn m'a compris, je veux bien un peu d'aide sur la
façon de procéder

Il y a sûrement mieux, mais tu obtiens déjà un début assez facilement avec :

---------------------------------------------------
pstr="""
{
val1 ...
};
"""
for s1 in pstr.replace('
','').replace('n','').strip().strip(';').split('{'):
for s2 in s1.rsplit('}'):
for s3 in s2.split(';'):
print s3
---------------------------------------------------

qui donne :
---------------------------------------------------
python testParser.py


val1="kljlk"
val23
val3 val12#
val22="popo"


vallist=(
te
ml

,
ml#
te6

)


---------------------------------------------------

Avatar
Sébastien Ramage
hum c'est bien vu

mais je viens de me rendre compte que j'ai dautre type de balise
les simples du type:
toto = "niania"
peuvent contenir des "{}" exemple : toto = "{niania}" mais c'est pas
le pire
le pire ce sont les balises liste sont du type:
liste = (
{
b1 = qqch;
b2 = qqch;
}
);

mais aussi sous cette forme
liste = (
"texte",
"texte",
"sdgsg" = "gsqgs",
(valeur),
(texte),
{
balise = qqc;
},
{
autre balise = qqc;
}
);

ça devient galère
Avatar
loufoque

pfiou, bref si qqn m'a compris, je veux bien un peu d'aide sur la
façon de procéder



Tu pourrais faire ça avec des expressions régulières récursives, ce qui
serait relativement peu performant et qui n'est pas supporté par Python.
Sinon c'est pas compliqué, tu pars à la recherche des caractères
délimiteurs { et } et c'est parti...

Au final tu veux quoi ? Une liste ? Un API de type SAX ou Pull ?

Avatar
jean-michel bain-cornu
Sébastien Ramage wrote:

ça devient galère

Il faut de l'huile de clavier !

Il me semble que vu la complexité, tu ne pourras pas éviter un balayage
de ton texte : du début à la fin pour les {, et de la fin au début pour
les } correspondants, avec une fonction qui s'auto-appelle (récursive)
dès qu'elle a isolé un bloc {...} (ce qui permet de traiter les
sous-blocs éventuels).
Ceci en l'absence d'une solution miracle dégotée par un big brain.
Pas très dur à faire AMAHA, il faut juste un peu de temps...

Bon courage,
jm

PS: AMAHA= A mon avis hautement autorisé ;-)

Avatar
Do Re Mi chel La Si Do
Bonjour !

Je ne me suis jamais attaqué à du vrai parsing.

Mais, j'avais repéré ça (en français) :
http://christophe.delord.free.fr/fr/tpg/index.html

En espérant que ça convienne...


@-salutations

Michel Claveau
Avatar
Sébastien Ramage
j'ai regardé TPG et honnetement, je ne comprends pas le fonctionnement
j'ai laissé tombé.


Il me semble que vu la complexité, tu ne pourras pas éviter un balaya ge
de ton texte : du début à la fin pour les {, et de la fin au début p our
les } correspondants, avec une fonction qui s'auto-appelle (récursive)
dès qu'elle a isolé un bloc {...} (ce qui permet de traiter les
sous-blocs éventuels).


tu pourrais détaillé un peu le principe, ça m'interesse

pour le moment je suis resté sur une batterie de .replace() et ça
fonctionne presque, je converti chaque balise {} en objet dict car la
syntaxe est très proche et les liste en list
mais j'aurai préféré avoir des objets pour chaque balise plutot que
dict dans des dict dans des list dans un dic.... ça devient vite
galère

Avatar
Paul Gaborit
À (at) 16 Dec 2005 04:05:48 -0800,
"Sébastien Ramage" écrivait (wrote):
j'ai regardé TPG et honnetement, je ne comprends pas le fonctionnement
j'ai laissé tombé.


Il me semble que vu la complexité, tu ne pourras pas éviter un balayage
de ton texte : du début à la fin pour les {, et de la fin au début pour
les } correspondants, avec une fonction qui s'auto-appelle (récursive)
dès qu'elle a isolé un bloc {...} (ce qui permet de traiter les
sous-blocs éventuels).


tu pourrais détaillé un peu le principe, ça m'interesse

pour le moment je suis resté sur une batterie de .replace() et ça
fonctionne presque, je converti chaque balise {} en objet dict car la
syntaxe est très proche et les liste en list


À mon avis, vous auriez tout intérêt à vous pencher sur des solutions
comme PLY (Python Lex Yacc) :

<http://www.dabeaz.com/ply/>

--
Paul Gaborit - <http://perso.enstimac.fr/~gaborit/>


Avatar
bruno at modulix
Sébastien Ramage wrote:
hum c'est bien vu

mais je viens de me rendre compte que j'ai dautre type de balise
les simples du type:
toto = "niania"
peuvent contenir des "{}" exemple : toto = "{niania}" mais c'est pas
le pire
le pire ce sont les balises liste sont du type:
liste = (
{
b1 = qqch;
b2 = qqch;
}
);

mais aussi sous cette forme
liste = (
"texte",
"texte",
"sdgsg" = "gsqgs",
(valeur),
(texte),
{
balise = qqc;
},
{
autre balise = qqc;
}
);

ça devient galère


Ca devient un peu beaucoup pour un parseur 'fait-main', surtout si tu
veux récupérer une arborescence d'objet et pas un dict de listes de
dicts de tuples contenant des listes de dict etc.

La bonne nouvelle, c'est qu'il existe pas mal de générateurs de parseurs
en Python. La mauvaise nouvelle, c'est qu'il faut comprendre comment ça
marche, et être capable de décrire la grammaire de ton langage.


--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in ''.split('@')])"

Avatar
tiissa

À mon avis, vous auriez tout intérêt à vous pencher sur des solut ions
comme PLY (Python Lex Yacc) :

<http://www.dabeaz.com/ply/>


PLY est en effet une tres bonne solution, d'autant plus si vous avez
deja utilise Lex et Yacc et avez des notions de compilation.

Mais, pour ce probleme precis, je conseillerais tout de meme plus
simplement l'utilisation de pyparsing [1] (pour information, il y a un
paquet debian).

Rapidement, on pourrait faire ca :

t_nom = Word(alphas+nums)
t_entier = Word(nums)
t_chaine = '"' + CharsNotIn('"') + '"'
t_enfant = Forward()
t_liste = '(' + delimitedList(t_enfant, ',') + ')'
t_valeur = t_entier | t_chaine | t_enfant | t_liste
t_affectation = t_nom + '=' + t_valeur + ';'
t_enfant << '{' + OneOrMore(t_affectation) + '}'


En deux mots, on definit un nom (un mot contenant des lettres et des
chiffres par exemple), une valeur, une affectation... et la grammaire
qui les relie.
Par exemple, une liste est simplement une liste d'enfants delimites par
des virgules placee entre parentheses . Un enfant est une liste
d'affectations entre accolades (on note la declaration avant de
specifier sa construction). Une affectation, c'est nom = valeur
Sur l'exemple, on obtient :

chaine_de_test = """
{
val1 = "kljlk";
val2 = 133;
val3 = {
val12 = 23;
val22 = "popo";
};

vallist = (
{
te = 13;
ml = 12;
},
{
ml = 23;
te = 36;
}
);

};
"""

print t_enfant.parseString(chaine_de_test)
['{', 'val1', '=', '"', 'kljlk', '"', ';', 'val2', '=', '133', ';',



'val3', '=', '{', 'val12', '=', '23', ';', 'val22', '=', '"', 'popo',
'"', ';', '}', ';', 'vallist', '=', '(', '{', 'te', '=', '13', ';',
'ml', '=', '12', ';', '}', '{', 'ml', '=', '23', ';', 'te', '=', '36',
';', '}', ')', ';', '}']


Pas tres lisible, mais c'est deja tout bien separe. On peut eclaircir
un peu en supprimant les ponctuations et en regroupant les listes et
les enfants (qui sont des blocs tant que j'y pense) :

# ponctuation que l'on veut ne pas afficher
t_PAR_O = Literal('(').suppress()
t_PAR_F = Literal(')').suppress()
t_ACC_O = Literal('{').suppress()
t_ACC_F = Literal('}').suppress()
t_PV = Literal(';').suppress()

t_nom = Word(alphas+nums)

t_entier = Word(nums)
t_chaine = Combine('"' + CharsNotIn('"') + '"')
t_enfant = Forward()
# les listes seront regroupees dans une liste
t_liste = Group(t_PAR_O + delimitedList(t_enfant, ',') + t_PAR_F)

t_valeur = t_entier | t_chaine | t_enfant | t_liste

t_affectation = t_nom + '=' + t_valeur + t_PV

# un enfant sera regroupe dans une liste
t_enfant << Group(t_ACC_O + OneOrMore(t_affectation) + t_ACC_F)


Et maintenant, c'est tout propre et la hierarchie est dans la structure
de liste :

print t_enfant.parseString(chaine_de_test)
[['val1', '=', '"kljlk"', 'val2', '=', '133', 'val3', '=', ['val12',



'=', '23',
'val22', '=', '"popo"'], 'vallist', '=', [['te', '=', '13', 'ml', ' =',
'12'], ['ml', '=', '23', 'te', '=', '36']]]]


Voila, maintenant la question a se poser est qu'est-ce qu'un nom (a
quels caractere on a droit), qu'est-ce qu'une valeur (et qu'est-ce
qu'une chaine de caracteres), etc.
Mais voila deja une base assez simple. Evidemment, je vous renvoie a la
doc de pyparsing [2], qui n'est pas geniale (et en anglais mais bon)
mais qu'il faut lire pour savoir ce qu'on peut faire. ;)


[1] http://pyparsing.sourceforge.net/
[2]
http://sourceforge.net/docman/display_doc.php?docid 578&group_id—203



Avatar
Sébastien Ramage
Merci beaucoup pour cet exemple très complet

je vais m'y intéresser de plus près car là j'avoue que j'avais
renoncer à lire une partie de mon fichier.

Merci à vous tous maintenant il me reste du travail, beaucoup de
travail

Seb
1 2