#!/usr/bin/env python
# -*-coding:utf-8 -*-
"""
Description
-----------
Ce module contient quelques fonctions simples pour lire un fichier xml. Les fonctions
permettent à partir d'une liste de ligne xml ou d'un fichier xml, d'extraire un bloc
de données contenu entre deux balises, d'obtenir les attributs et les valeurs associées à
une balise, d'extraire les données entre deux balises et d'obtenir le nom d'une
balise.
Vocabulaire
-----------
Par la suite on emploiera les termes ``balise``, ``clefs``, ``valeur`` et ``bloc``. Voici
ce qu'on appellera un ``bloc`` et qui définit les autres terme ::
<balise clefs1="valeur" clefs2="valeur">
bla bla
</balise>
Dans les exemples, on travaille sur un fichier xml comme celui ci ::
<monxml>
<info>
<i> coucou </i>
<age nom="toto"> 18 </age>
<taille nom="tata"> 160 </taille>
</info>
<param>
<allsets>
<set info="att 1" type="a">
<i> 1. </i>
</set>
<set info="att 2" type="b">
<i> 2. </i>
</set>
<set info="att 3" type="a">
<i> 3. </i>
</set>
</allsets>
</param>
</monxml>
"""
xml = """<monxml>
<info>
<i> coucou </i>
<age nom="toto"> 18 </age>
<taille nom="tata"> 160 </taille>
</info>
<param>
<allsets>
<set info="att 1" type="a">
<i> 1. </i>
</set>
<set info="att 2" type="b">
<i> 2. </i>
</set>
<set info="att 3" type="a">
<i> 3. </i>
</set>
</allsets>
</param>
</monxml>""".split("\n")
import doctest
__licence__ = "GPL"
__author__ = "Germain Vallverdu <germain.vallverdu@univ-pau.fr>"
[docs]def getBlocs(balise, liste, clefs = None, onlyfirst = False):
""" Renvoie une liste dont chaque élément contient la liste des lignes correspondantes
aux blocs délimités par balise, c'est à dire comprises entre ``<balise>`` et
``</balise>``.
:param balise: balise name
:type balise: string
:param liste: xml input file
:type liste: list or file object
:param clefs: dictionnary of ``{clefs: valeur}``, if clefs is not None, only blocs
with these clefs et valeurs are returned.
:type clefs: dict
:param onlyfirst: If true, only the first bloc is returned
:type onlyfirst: bool
Examples using the xml sample show at the top of this page :
>>> xml = open("fichier.xml", "r").readlines()
You want bloc ``info``
>>> getBlocs("info", xml)
[['<info>', '<i> coucou </i>', '<age nom="toto"> 18 </age>', '<taille nom="tata"> 160 </taille>', '</info>']]
You want blocs ``set``
>>> getBlocs("set", xml)
[['<set info="att 1" type="a">', '<i> 1. </i>', '</set>'], ['<set info="att 2" type="b">', '<i> 2. </i>', '</set>'], ['<set info="att 3" type="a">', '<i> 3. </i>', '</set>']]
You want the first bloc ``set``
>>> getBlocs("set", xml, onlyfirst = True)
['<set info="att 1" type="a">', '<i> 1. </i>', '</set>']
You want bloc ``set`` with clefs ``type`` equal ``b`` and clefs ``info`` equal ``att
2``
>>> getBlocs("set", xml, {"type":"b", "info":"att 2"})
[['<set info="att 2" type="b">', '<i> 2. </i>', '</set>']]
You want bloc ``set`` with clefs ``type`` equal ``a``
>>> getBlocs("set", xml, {"type":"a"})
[['<set info="att 1" type="a">', '<i> 1. </i>', '</set>'], ['<set info="att 3" type="a">', '<i> 3. </i>', '</set>']]
"""
# defini la variable de retour comme nulle
blocs = list()
# delimiteurs du bloc
openbloc = "<" + balise.strip()
closebloc = "</" + balise.strip() + ">"
# liste des lignes en retour
start = False
nbloc = 0
for ligne in liste:
if openbloc in ligne:
if not start:
if clefs is not None:
# cherche les attributs
valeursAttributs = getClefs( ligne, clefs.keys() )
# verifie la valeur des attributs
for att in clefs.keys():
if clefs[ att ] == valeursAttributs[ att ]:
start = True
else:
break
if start:
nopen = 1
nclose = 0
nbloc += 1
blocs.append( list() )
else :
nopen = 1
nclose = 0
start = True
nbloc += 1
blocs.append( list() )
else:
nopen += 1
if closebloc in ligne and start == True :
blocs[nbloc - 1].append(ligne.strip())
nclose += 1
if nclose == nopen:
start = False
if onlyfirst:
break
else:
continue
if start :
blocs[nbloc - 1].append(ligne.strip())
# return None if balise not found
if len(blocs) == 0:
blocs = None
# si un seul bloc demandé, retourne la liste des lignes uniquement
if onlyfirst and blocs != None:
blocs = blocs[0]
return blocs
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
[docs]def getClefs(ligne, listeClefs):
""" Retourne dans un dictionnaire, les valeurs des clefs indiquées dans ``listeClefs``
lues sur ``ligne``. Si une clefs n'est pas trouvée la valeur None lui est attribuée.
:param ligne: xml input line
:type ligne: string
:param listeClefs: liste des clefs dont on veut les valeurs
:type listeClefs: list
>>> ligne = '<i type="string" name="PREC">medium</i>'
>>> getClefs(ligne, ["type", "name"])
{'type': 'string', 'name': 'PREC'}
"""
clefs = dict()
for att in listeClefs:
pos = ligne.find( str(att) )
if pos == -1:
clefs[str(att)] = None
else :
k = ligne[pos:].find("\"")
k += pos + 1
l = ligne[k:].find("\"")
l += k
if l < k:
clefs[str(att)] = None
else:
clefs[ str(att) ] = ligne[k:l]
return clefs
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
[docs]def getNodeData(ligne):
""" Retourne les données contenues entre deux balises xml lues sur ``ligne``
:param ligne: xml input line
:type ligne: string
Examples :
>>> ligne = '<i type="string" name="PREC">medium</i>'
>>> getNodeData(ligne)
['medium']
>>> ligne = '<i name="subversion" type="string">3Apr08 complex serial</i>'
>>> getNodeData(ligne)
['3Apr08', 'complex', 'serial']
>>> ligne = '<i name="ENCUT"> 450.00000000</i>'
>>> getNodeData(ligne)
['450.00000000']
"""
debutValeurs = False
tmpValeurs = list()
for c in ligne:
if c == "<":
debutValeurs = False
continue
if c == ">":
debutValeurs = True
continue
if debutValeurs :
tmpValeurs.append(c)
valeurs = "".join(tmpValeurs)
return valeurs.split()
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
[docs]def getNodeName( ligne ) :
""" Retourne le nom de la balise sur une ligne d'un fichier xml
:param ligne: xml input line
:type ligne: string
Examples :
>>> ligne = "<kpoints></kpoints>"
>>> getNodeName(ligne)
'kpoints'
>>> ligne = '<i name="subversion" type="string">3Apr08 complex serial</i>'
>>> getNodeName(ligne)
'i'
"""
k = ligne.find("<")
l = ligne.find(">")
return ligne[k+1:l].split()[0].strip()
# * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
if __name__ == "__main__":
print(__doc__)
print("LANCEMENT DES TESTS :")
print(" doctest.testmod()\n")
tests = doctest.testmod()
print(tests)
print("\nfin")