OVH Cloud OVH Cloud

Lire des dict depuis un csv

9 réponses
Avatar
Nomak
Bonjour,

je suis un nouvel utilisateur de python, et je butte sur un problème.

J'ai une base de données, qui contient des enregistrement de tailles
variables, et de clés variables. Pour l'instant je sauvegarde et je lis
comme ceci:

def LoadFrom(self, filename):
try:
reader = file(filename)
for row in reader:
a = eval(row)
assert a.__class__ == dict
self.rows.append(a)
except:
print 'File ' + filename + ' could not be found'

def SaveTo(self, filename):
try:
writer = file(filename, 'w')
for a in self.rows:
assert a.__class__ == dict
print >> writer, a
except:
print 'Unable to save to file ' + filename


Mais utiliser 'eval' n'est pas très sûr, donc je me demandais s'il
existait un moyen d'utiliser le format csv. Mais le module csv fourni
avec python (2.3) semble être fait pour les lignes dont on connait à
l'avance le nombre d'enregistrement.

Est-ce que qqn sait comment faire?

9 réponses

Avatar
News M Claveau /Hamster-P
Bonjour !

Pour sauvegarder/lire facilement un objet de ce genre (dict), je te suggère
de regarder Pickle et cPickle.

@-salutations

Michel Claveau
Avatar
bruno modulix
Nomak wrote:
Bonjour,

je suis un nouvel utilisateur de python, et je butte sur un problème.

J'ai une base de données,


Qui vient d'où ? (je veux dire : c'est un format ad hoc propre à ton
appli, ça vient d'une autre appli, c'est fourni par l'utilisateur, etc?)

qui contient des enregistrement de tailles
variables, et de clés variables. Pour l'instant je sauvegarde et je lis
comme ceci:

def LoadFrom(self, filename):
try:
reader = file(filename)
for row in reader:
a = eval(row)
assert a.__class__ == dict
self.rows.append(a)
except:
print 'File ' + filename + ' could not be found'


<hs topic='coding-style'>
1/ Evite les except 'bruts'. Précise le type d'exception que tu entends
gérer - ici, IOError. D'autant que le problème n'est pas forcément lié à
l'absence du fichier.
2/ La sortie standard (stdout) est pour les sorties 'normales'. Les
erreurs vont sur la sortie d'erreur (stderr).


except IOError, e:
err = "Could not open %s for reading : %s" % (filename, e)
print >> sys.stderr, msg

(nécessite d'importer sys)
</hs>

def SaveTo(self, filename):
try:
writer = file(filename, 'w')
for a in self.rows:
assert a.__class__ == dict
print >> writer, a
except:
print 'Unable to save to file ' + filename


Mais utiliser 'eval' n'est pas très sûr,


Non, en effet.

donc je me demandais s'il
existait un moyen d'utiliser le format csv. Mais le module csv fourni
avec python (2.3) semble être fait pour les lignes dont on connait à
l'avance le nombre d'enregistrement.


s/enregistrement/champs/

Effectivement, une des caractéristiques d'un format CSV est que tous les
enregistrements (lignes) ont la même structure (même nombre de champs,
dans le même ordre).

Est-ce que qqn sait comment faire?


1/ Ecrire ton propre parser.
2/ ???

Difficile d'en dire plus sans plus d'infos (source des fichiers, format
de fichier, nécessité d'avoir des fichiers portables et/ou lisibles par
un humain, etc)

Si c'est un format perso, uniquement lu et écrit par ton appli, tu peux
soit utiliser un format CSV, soit utiliser Pickle/Shelve/..., soit
utiliser un système de gestion de données - berkley, SQL 'embedded',
serveur SQL, ZODB, etc)

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

Avatar
J.Pierre Pourrez
Le Sun, 20 Mar 2005 15:07:31 +0100, News M Claveau /Hamster-P a écrit :

Pour sauvegarder/lire facilement un objet de ce genre (dict), je te
suggère de regarder Pickle et cPickle.


Le module shelve est encore plus simple.
Les objets sont stockés dans un dictionnaire et le dictionnaire est
lu/stocké dans un fichier de façon transparente :
http://docs.python.org/lib/module-shelve.html

Cordialement
Jean-Pierre

Avatar
Do Re Mi chel La Si Do
Bonjour !


C'est vrai, j'ai "oublié" shelve. Alors que ce module est vraiment simple
d'utilisation.


@-salutations
--
Michel Claveau
Avatar
Nomak
bruno modulix wrote:
Bonjour,

je suis un nouvel utilisateur de python, et je butte sur un problème.

J'ai une base de données,



Qui vient d'où ? (je veux dire : c'est un format ad hoc propre à ton
appli, ça vient d'une autre appli, c'est fourni par l'utilisateur, etc?)


c'est un format perso, mais que je veux garder simple et intuitif (1
ligne = 1 enregistrement)

qui contient des enregistrement de tailles variables, et de clés
variables. Pour l'instant je sauvegarde et je lis comme ceci:

def LoadFrom(self, filename):
try:
reader = file(filename)
for row in reader:
a = eval(row)
assert a.__class__ == dict
self.rows.append(a)
except:
print 'File ' + filename + ' could not be found'



<hs topic='coding-style'>
1/ Evite les except 'bruts'. Précise le type d'exception que tu entends
gérer - ici, IOError. D'autant que le problème n'est pas forcément lié à
l'absence du fichier.
2/ La sortie standard (stdout) est pour les sorties 'normales'. Les
erreurs vont sur la sortie d'erreur (stderr).


except IOError, e:
err = "Could not open %s for reading : %s" % (filename, e)
print >> sys.stderr, msg
^^^ err


(nécessite d'importer sys)
</hs>


merci pour le conseil


def SaveTo(self, filename):
try:
writer = file(filename, 'w')
for a in self.rows:
assert a.__class__ == dict
print >> writer, a
except:
print 'Unable to save to file ' + filename


Mais utiliser 'eval' n'est pas très sûr,



Non, en effet.

donc je me demandais s'il existait un moyen d'utiliser le format csv.
Mais le module csv fourni avec python (2.3) semble être fait pour les
lignes dont on connait à l'avance le nombre d'enregistrement.



s/enregistrement/champs/

Effectivement, une des caractéristiques d'un format CSV est que tous les
enregistrements (lignes) ont la même structure (même nombre de champs,
dans le même ordre).

Est-ce que qqn sait comment faire?



1/ Ecrire ton propre parser.


surement pas... j'en ai érit suffisemmment jusque'ici. Plus jamais!!!

2/ ???

Difficile d'en dire plus sans plus d'infos (source des fichiers, format
de fichier, nécessité d'avoir des fichiers portables et/ou lisibles par
un humain, etc)

Si c'est un format perso, uniquement lu et écrit par ton appli, tu peux
soit utiliser un format CSV, soit utiliser Pickle/Shelve/..., soit
utiliser un système de gestion de données - berkley, SQL 'embedded',
serveur SQL, ZODB, etc)


source = le soft lui même
format = simple (mode texte) et intuitif, facile à importer dans un
autre soft si besoin
lisible par un humain = oui, ne serais ce que pour le debug
serveur = non surtout pas


Je vais voir du côté de shelve, merci bcp pour toutes ces infos


Avatar
Bruno Desthuilliers
bruno modulix wrote:

Bonjour,

je suis un nouvel utilisateur de python, et je butte sur un problème.

J'ai une base de données,


Qui vient d'où ? (je veux dire : c'est un format ad hoc propre à ton
appli, ça vient d'une autre appli, c'est fourni par l'utilisateur, etc?)



c'est un format perso, mais que je veux garder simple et intuitif (1
ligne = 1 enregistrement)


Dans ce cas, CSV est le plus simple. Mais ça implique des
enregistrements à taille et structure fixe.

(snip)
2/ La sortie standard (stdout) est pour les sorties 'normales'. Les
erreurs vont sur la sortie d'erreur (stderr).


except IOError, e:
err = "Could not open %s for reading : %s" % (filename, e)
print >> sys.stderr, msg
^^^ err



oops :(

(snip)
donc je me demandais s'il existait un moyen d'utiliser le format csv.
Mais le module csv fourni avec python (2.3) semble être fait pour les
lignes dont on connait à l'avance le nombre d'enregistrement.


s/enregistrement/champs/

Effectivement, une des caractéristiques d'un format CSV est que tous
les enregistrements (lignes) ont la même structure (même nombre de
champs, dans le même ordre).

Est-ce que qqn sait comment faire?


1/ Ecrire ton propre parser.


surement pas... j'en ai érit suffisemmment jusque'ici. Plus jamais!!!


Hé, j'ai pas parlé de réécrire Yacc !-)

Ceci étant, il y a pas mal de générateurs de parser en Python... Si tu a
besoin de définir une grammaire un peu complexe, ça reste une solution
envisageable. Mais bon, s'il y a plus simple, hein ?...

2/ ???

Difficile d'en dire plus sans plus d'infos (source des fichiers,
format de fichier, nécessité d'avoir des fichiers portables et/ou
lisibles par un humain, etc)

Si c'est un format perso, uniquement lu et écrit par ton appli, tu
peux soit utiliser un format CSV, soit utiliser Pickle/Shelve/...,
soit utiliser un système de gestion de données - berkley, SQL
'embedded', serveur SQL, ZODB, etc)


source = le soft lui même
format = simple (mode texte) et intuitif, facile à importer dans un
autre soft si besoin
lisible par un humain = oui, ne serais ce que pour le debug
serveur = non surtout pas

Je vais voir du côté de shelve, merci bcp pour toutes ces infos


Si tu veux un format "simple et intuitif, lisible par un humain", je
doute que tu y trouve ton bonheur (ou alors on n'a pas la même
définition de ces termes !-).

Je n'aime pas spécialement XML (je suppose que toi non plus, sinon tu
aurais probablement déjà fait ce choix...), mais vu certaines de tes
specs (mode texte, interopérable, lisible par un humain, ne nécessitant
pas l'écriture d'un parser maison, pouvant gérer des structures
complexes), ça peut être une solution à envisager. Ok, ce n'est pas
spécialement 'simple et intuitif', et tu n'a pas forcément une ligne par
enregistrement, mais c'est suffisament standard pour ne pas nécessiter
un apprentissage supplémentaire et garantir une bonne interopérabilité.

Si tu veux vraiment une logique 'une ligne par enregistrement', tu a le
choix entre un vrai CSV - auquel cas il te faut soit autant de champs
que de variantes possibles dans tes formats de données plus un champs
indiquant le format effectif pour chaque ligne, soit autant de fichiers
que de formats différents (mais à ce stade, autant utiliser une base
SQL) - et un format perso - qui nécessite un parser perso, une doc, etc...

Désolé, pas mieux... Quelqu'un d'autre ?

Bruno



Avatar
tiissa
Tout d'abord, bonjour à tous puisque c'est mon premier message ici. :)

Nomak wrote:
1/ Ecrire ton propre parser.


surement pas... j'en ai érit suffisemmment jusque'ici. Plus jamais!!!
En même temps, parser du csv c'est principalement utiliser split.

Surtout si les données sont simples.

Du coup la lecture pourrait ressembler à :

########
def LoadFrom(self, filename, keys=None):
"""Load a csv file as a list of dictionary in self.rows.
If the list of keys are not supplied, take them from the first line."""

reader=open(filename,'r')
if isNone(keys):
# '[:-1]' to get rid of the newline character
keys=reader.readline()[:-1].split(',')

# assign its index to each key
# (for dictionary building next loop)
index=[(i,keys[i]) for i in range(len(keys))]

for line in reader:
row=line[:-1].split(',')
d={}
for i,k in index:
# insert some error checking there
d[k]=row[i]
self.rows.append(d)
reader.close()
########

Bon, il manque la gestion d'erreur (comme vérifier qu'il y a le bon
nombre de champs a chaque ligne) mais c'est facilement rajoutable. De
plus, ça ne gère pas les champs multi-ligne (là le parsing est plus
compliqué).

Personnellement, j'utilise pickle pour mes fichiers. Ca évite les
soucis. Si tu veux un format plus lisible mais plus souple que csv, tu
peux regarder le xml et ses nombreux outils.
Une autre alternative serait d'utiliser pickle et de faire un petit
outil d'édition adapté à ta liste de dictionnaires.


Avatar
F. Petitjean
Tout d'abord, bonjour à tous puisque c'est mon premier message ici. :)

Nomak wrote:
1/ Ecrire ton propre parser.


surement pas... j'en ai érit suffisemmment jusque'ici. Plus jamais!!!
En même temps, parser du csv c'est principalement utiliser split.

Surtout si les données sont simples.

Du coup la lecture pourrait ressembler à :

########
def LoadFrom(self, filename, keys=None):
"""Load a csv file as a list of dictionary in self.rows.
If the list of keys are not supplied, take them from the first line."""

reader=open(filename,'r')
if isNone(keys):
# '[:-1]' to get rid of the newline character
keys=reader.readline()[:-1].split(',')

# assign its index to each key
# (for dictionary building next loop)
index=[(i,keys[i]) for i in range(len(keys))]

for line in reader:
row=line[:-1].split(',')
d={}
for i,k in index:
# insert some error checking there
d[k]=row[i]
self.rows.append(d)
reader.close()
########

Bon, il manque la gestion d'erreur (comme vérifier qu'il y a le bon
nombre de champs a chaque ligne) mais c'est facilement rajoutable. De
plus, ça ne gère pas les champs multi-ligne (là le parsing est plus
compliqué).
<mode emmerdeur="on">

vous utilisez le fait qu'avec un python moderne ce qui est retrourné
par open() est un itérateur avec for line in reader: mais
précédemment vous vous permettez de d'utiliser reader.readline(). Ce
n'est pas très rigoureux.(itreader = iter(reader) et itreader.next()
à la place du readline() serait plus juste, suivi naturellement par in
for line in itreader: )
De plus si vous supposez avoir une version
suffisamment récente de Python la ligne
index=[(i,keys[i]) for i in range(len(keys))]
s'écrit
index = list(enumerate(keys))
ou
# index = enumerate(keys)
selon que vous voulez ou non la laisser à l'extérieur de la boucle ou
réinitialiser l'itérateur dans la boucle.
</mode>

Personnellement, j'utilise pickle pour mes fichiers. Ca évite les
soucis. Si tu veux un format plus lisible mais plus souple que csv, tu
peux regarder le xml et ses nombreux outils.
Une autre alternative serait d'utiliser pickle et de faire un petit
outil d'édition adapté à ta liste de dictionnaires.




Avatar
tiissa
F. Petitjean wrote:
vous utilisez le fait qu'avec un python moderne ce qui est retrourné
par open() est un itérateur avec for line in reader: mais
précédemment vous vous permettez de d'utiliser reader.readline(). Ce
n'est pas très rigoureux.(itreader = iter(reader) et itreader.next()
à la place du readline() serait plus juste, suivi naturellement par in
for line in itreader: )
En effet. J'ai découvert très récemment qu'on pouvait utiliser un

fichier de cette façon et mon style s'adapte lentement à cette écriture.

De plus si vous supposez avoir une version
suffisamment récente de Python la ligne
index=[(i,keys[i]) for i in range(len(keys))]
s'écrit
index = list(enumerate(keys))
ou
# index = enumerate(keys)
selon que vous voulez ou non la laisser à l'extérieur de la boucle ou
réinitialiser l'itérateur dans la boucle.
Je ne connaissais pas enumerate. J'en prend bonne note.


</mode>
Merci bien. J'ai commencé python il n'y a pas si longtemps et mon

inscription à ce newsgroup est en partie motivée par la poursuite de mon
apprentissage. C'est bien parti. :)