Optimisation

Le
pil91
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.
Le problème c'est que ces fichiers peuvent être très gros, plusieurs
millions de lignes et plusieurs milliers de caractères par ligne.
J'ai donc écrit ce code sous windows en Python 2.5.2, qui, je pense
pourrait être bien optimisé.
Merci d'avance pour vos suggestions.


# _*_ coding: utf8 _*_
#!/usr/bin/env python
# Auteur : Philippe Lotton
# Date de création:

import sys
import datetime
if __name__ == '__main__':
args = sys.argv[1:]
if len(args) == 2: # 2 paramètres obligatoires
fic_entree = args[0] # Fichier source
fic_format = args[1] #Fichier format
else:
print '''Vous devez passez 2 arguments, le fichier source et
le fichier paramètre
ex: csv.py MonFic.txt MonFormat.txt
Le fichier source doit avoir des enregistrements de longueurs
fixes.
Le fichier format doit ètre de la forme: position , longueur
<rc>,
autant de ligne que de champs dans le fichier source.
'''
exit()
print datetime.datetime.now().time()
fic_param = open('format.txt', "r") # Lecture du fichier
paramètre
format = fic_param.readlines() # Création tableaux
des paramètres
fic_param.close()

fic_out = open('csv.txt', 'w') #Ouverture fichier de sortie

fic_source = open(fic_entree, 'r') # Lecture du fichier
source
while 1: #
ligne_source = fic_source.readline()
if ligne_source =='':
break
else :
ligne_cible = '' # Ligne de sortie écrite dans
le fichier
for j in format:
params = j.split(',') #Création d'un
tableau de paramètres
pos1 = int(params[0])-1 #Position 1
pos2 = pos1 + int(params[1]) #Position 2
temp = ligne_source[ pos1:pos2]
ligne_cible = ligne_cible + temp + ',' #
Concaténation
ligne_cible = ligne_cible[0:-1]
fic_out.write(ligne_cible + "")
ligne_cible = ''
fic_source.close()
fic_out.close()
print datetime.datetime.now().time()
exit()
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 3
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Christophe
Le #16776371
pil91 a écrit :
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.
Le problème c'est que ces fichiers peuvent être très gros, plusieurs
millions de lignes et plusieurs milliers de caractères par ligne.
J'ai donc écrit ce code sous windows en Python 2.5.2, qui, je pense
pourrait être bien optimisé.
Merci d'avance pour vos suggestions.

======== > # _*_ coding: utf8 _*_
#!/usr/bin/env python
# Auteur : Philippe Lotton
# Date de création:

import sys
import datetime



C'est pas une suggestion d'optim (enfin, ça pourrait le devenir) mais y
a-t-il une raison pour laquelle vous n'utilisez pas le module standard
csv ? Au début, je pensais que vous aviez écrit votre solution pour
justement être plus performant mais en regardant le source de csv.py, je
constate que celui-ci délègue la majorité du travail à un module et C.

En fait si, c'est bien une suggestion d'optim après tout.
Bruno Desthuilliers
Le #16776591
pil91 a écrit :
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.
Le problème c'est que ces fichiers peuvent être très gros, plusieurs
millions de lignes et plusieurs milliers de caractères par ligne.
J'ai donc écrit ce code sous windows en Python 2.5.2, qui, je pense
pourrait être bien optimisé.



Merci d'avance pour vos suggestions.

======== > # _*_ coding: utf8 _*_
#!/usr/bin/env python
# Auteur : Philippe Lotton
# Date de création:

import sys
import datetime
if __name__ == '__main__':



Déjà, découpe ton code en fonctions.

args = sys.argv[1:]
if len(args) == 2: # 2 paramètres obligatoires
fic_entree = args[0] # Fichier source
fic_format = args[1] #Fichier format
else:
print '''Vous devez passez 2 arguments, le fichier source et
le fichier paramètre
ex: csv.py MonFic.txt MonFormat.txt
Le fichier source doit avoir des enregistrements de longueurs
fixes.
Le fichier format doit ètre de la forme: position , longueur
<rc>,
autant de ligne que de champs dans le fichier source.
'''



les messages d'erreurs devraient aller sur la sortie d'erreur, pas sur
la sortie standard. Donc, print >> sys.stderr.

Ceci étant, puisque sys.exit(msg) écrit le message sur stderr, tu peux
tout simplement passer ton message à sys.exit().

exit()
print datetime.datetime.now().time()
fic_param = open('format.txt', "r") # Lecture du fichier
paramètre



Pas la peine de commenter chaque ligne avec une paraphrase du code. On
est capable de lire le code. Si ton commentaire n'apporte rien, ne
commente pas.

Accessoirement, je suppose que tu veux:
fic_param = open(fic_format)

Et là, il apparaît que tu utilises la même convention de nommage pour
des chemins de fichier et des objets fichier. Combien de temps avant que
tu ne t'embrouilles, à ton avis ?

format = fic_param.readlines()
fic_param.close()

fic_out = open('csv.txt', 'w')
fic_source = open(fic_entree, 'r')
while 1:
ligne_source = fic_source.readline()
if ligne_source =='':
break
else :



Les fichiers en lecture sont leur propre itérateur. Ca t'évite un test,
au passage...

for ligne_source in fic_source:


ligne_cible = '' # Ligne de sortie écrite dans
le fichier
for j in format:
params = j.split(',') #Création d'un
tableau de paramètres
pos1 = int(params[0])-1 #Position 1
pos2 = pos1 + int(params[1]) #Position 2



T'a pas l'impression que refaire ces calculs à chaque ligne du fichier
source est une perte de temps phénoménale ? Parse le tout avant d'entrer
dans la boucle, tu gagnera du temps.

temp = ligne_source[ pos1:pos2]
ligne_cible = ligne_cible + temp + ',' #
Concaténation



A éviter, tout particulièrement sous cette forme (la forme 'ligne +=
truc' est optimisée dans les versions récentes de CPython, mais ça reste
une mauvaise solution).

La bonne façon de faire est de stocker les champs dans une liste, et
faire un ",".join(laliste) à la fin.

ligne_cible = ligne_cible[0:-1]



ce qui évite aussi ça...


fic_out.write(ligne_cible + "n")
ligne_cible = ''



cette dernière instruction est inutile, tu le fais déjà au début de la
boucle.

fic_source.close()
fic_out.close()
print datetime.datetime.now().time()
exit()





Correction possible, mais Q&D et pas très robuste. J'ai gardé ton
nommage mais tu devrais le corriger. NB : pas testé.

import sys
import datetime

def parse_params(lines):
params = []
for line in enumerate(lines):
line = line.strip()
if not line:
continue
parts = line.split(',')
pos1 = int(parts[0])-1
pos2 = pos1 + int(parts[1])
params.append(slice(pos1, pos2))
return params


def do_import(params, fic_source, fic_out):
write = fic_out.write
sep = ','
join = sep.join # evite un lookup
for ligne_source in fic_source:
ligne_cible = [ligne_source[slice] for slice in params]
write(join(ligne_cible) + "n")

def main(args):
try:
fic_entree, fic_format = args
except ValueError:
sys.exit('''Vous devez passez 2 arguments, le fichier source et
le fichier paramètre
ex: csv.py MonFic.txt MonFormat.txt
Le fichier source doit avoir des enregistrements de longueurs
fixes.
Le fichier format doit ètre de la forme: position , longueur
<rc>,
autant de ligne que de champs dans le fichier source.
''')

# XXX : gérer le cas d'une exception durant le parsing
fic_param = open(fic_format)
params = parse_param(fic_param)
fic_params.close()

# XXX : gérer le cas d'une erreur lors de l'ouverture de fic_out
fic_source = open(fic_entree, 'r')
fic_out = open('csv.txt', 'w')
try:
do_import(params, fic_source, fic_out)
finally:
fic_source.close()
fic_out.close()

if __name__ == '__main__':
print >> sys.stderr, datetime.datetime.now().time()
main(sys.argv[1:])
print >> sys.stderr, datetime.datetime.now().time()



HTH
Bruno Desthuilliers
Le #16776581
Christophe a écrit :
pil91 a écrit :
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.




(snip)

C'est pas une suggestion d'optim (enfin, ça pourrait le devenir) mais y
a-t-il une raison pour laquelle vous n'utilisez pas le module standard
csv ?



Peut-être le fait que le format csv soit la cible, pas la source ?-)
Christophe
Le #16776571
Bruno Desthuilliers a écrit :
Christophe a écrit :
pil91 a écrit :
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.




(snip)

C'est pas une suggestion d'optim (enfin, ça pourrait le devenir) mais
y a-t-il une raison pour laquelle vous n'utilisez pas le module
standard csv ?



Peut-être le fait que le format csv soit la cible, pas la source ?-)



Et ? Le module csv fait lecture et écriture.



Help on module csv:

NAME
csv - CSV parsing and writing.

FILE
c:python25libcsv.py

DESCRIPTION
This module provides classes that assist in the reading and writing
of Comma Separated Value (CSV) files, and implements the interface
(...)
pil91
Le #16776561
On 10 sep, 17:03, Christophe
pil91 a écrit :



> Bonjour, j'ai besoin de transformer en format csv délimité, des
> fichiers fixes.
> Le problème c'est que ces fichiers peuvent être très gros, plusie urs
> millions de lignes et plusieurs milliers de caractères par ligne.
> J'ai donc écrit ce code sous windows en Python 2.5.2, qui, je pense
> pourrait être bien optimisé.
> Merci d'avance pour vos suggestions.

> =========
> # _*_ coding: utf8 _*_
> #!/usr/bin/env python
> # Auteur : Philippe Lotton
> # Date de création:

> import sys
> import datetime

C'est pas une suggestion d'optim (enfin, ça pourrait le devenir) mais y
a-t-il une raison pour laquelle vous n'utilisez pas le module standard
csv ? Au début, je pensais que vous aviez écrit votre solution pour
justement être plus performant mais en regardant le source de csv.py, j e
constate que celui-ci délègue la majorité du travail à un module et C.

En fait si, c'est bien une suggestion d'optim après tout.



Je pensais améliorer les choses en remplacant la concaténation de ma
chaine un append dans un tableau, puis un join
----
else :
sortie = []
for j in format:
params = j.split(',') #Création d'un
tableau de paramètres
pos1 = int(params[0])-1 #Position 1
pos2 = pos1 + int(params[1]) #Position 2
temp = ligne_source[ pos1:pos2]
sortie.append(temp)
result = ','.join(sortie)
out.write(result + "n")
sortie = []
fic_source.close()
out.close()
---
Excès d'autosatisfaction, c'est quif quif, 18s, sur 1 000 000
d'enregistrement avec cette structure
------
aaaaaaaaaaabbbbwyyyyyyyyyyyyyyy
tttttttttttppppbqqqqqqqqqqqqqqq
rrrrrrrrrrrhhhhkeeeeeeeeeeeeeee
-------
@Christophe
Oui c'est vrai le module csv, ben... j'ai essayé de l'importer et puis
après je suis passé à autre chose.
Quand je lancais l'exécution j'obtenais un message 'module object has
no attribute reader.
Bruno Desthuilliers
Le #16778111
Christophe a écrit :
Bruno Desthuilliers a écrit :
Christophe a écrit :
Bruno Desthuilliers a écrit :
Christophe a écrit :
pil91 a écrit :
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.




(snip)

C'est pas une suggestion d'optim (enfin, ça pourrait le devenir)
mais y a-t-il une raison pour laquelle vous n'utilisez pas le
module standard csv ?



Peut-être le fait que le format csv soit la cible, pas la source ?-)



Et ? Le module csv fait lecture et écriture.



Oui. Il lit effectivement des csv. Ce qui n'est pas le cas des
fichiers sources de l'OP, ni la source de son problème.




Ah, je m'excuse, j'avais mal vu l'énoncé et je confondais le format du
fichier de format avec le format du fichier d'entrée de données. Comme
quoi il est mal de répondre à ce genre de questions pendant les heures
de travail (même pendant une pause) : on va trop vite et on rate des
choses importantes :)



Pas de blème, ça arrive à tout le monde (moi le premier...) !-)
Christophe
Le #16776791
pil91 a écrit :
@Christophe
Oui c'est vrai le module csv, ben... j'ai essayé de l'importer et puis
après je suis passé à autre chose.
Quand je lancais l'exécution j'obtenais un message 'module object has
no attribute reader.




Ah mais chez moi il marche très bien :) Si le module était cassé à ce
point, ça se saurait.

Allez, je parie qu'il y a un petit fichier csv.py ou csv.pyc qui traine
pas loin et qui cache le module officiel.
pil91
Le #16776781
On 10 sep, 17:31, pil91
On 10 sep, 17:03, Christophe


> pil91 a écrit :

> > Bonjour, j'ai besoin de transformer en format csv délimité, des
> > fichiers fixes.
> > Le problème c'est que ces fichiers peuvent être très gros, plus ieurs
> > millions de lignes et plusieurs milliers de caractères par ligne.
> > J'ai donc écrit ce code sous windows en Python 2.5.2, qui, je pense
> > pourrait être bien optimisé.
> > Merci d'avance pour vos suggestions.

> > =========
> > # _*_ coding: utf8 _*_
> > #!/usr/bin/env python
> > # Auteur : Philippe Lotton
> > # Date de création:

> > import sys
> > import datetime

> C'est pas une suggestion d'optim (enfin, ça pourrait le devenir) mais y
> a-t-il une raison pour laquelle vous n'utilisez pas le module standard
> csv ? Au début, je pensais que vous aviez écrit votre solution pour
> justement être plus performant mais en regardant le source de csv.py, je
> constate que celui-ci délègue la majorité du travail à un modul e et C.

> En fait si, c'est bien une suggestion d'optim après tout.

Je pensais améliorer les choses en remplacant la concaténation de ma
chaine un append dans un tableau, puis un join
----
        else :
            sortie = []
            for j in format:
                params = j.split(',')                #Création d'un
tableau de paramètres
                pos1 = int(params[0])-1           #Position 1
                pos2 = pos1 + int(params[1])   #Posit ion 2
                temp = ligne_source[ pos1:pos2]
                sortie.append(temp)
            result = ','.join(sortie)
            out.write(result  + "n")
            sortie = []
    fic_source.close()
    out.close()
---
Excès d'autosatisfaction, c'est quif quif, 18s,  sur 1 000 000
d'enregistrement avec cette structure
------
aaaaaaaaaaabbbbwyyyyyyyyyyyyyyy
tttttttttttppppbqqqqqqqqqqqqqqq
rrrrrrrrrrrhhhhkeeeeeeeeeeeeeee
-------
@Christophe
Oui c'est vrai le module csv, ben... j'ai essayé de l'importer et puis
après je suis passé à autre chose.
Quand je lancais l'exécution j'obtenais un message 'module object has
no attribute reader.



@Bruno Desthuilliers
Merci pour ce cours magistral, je revois ma copie demain :).
Effectivement l'utilisation du module csv serait plus orthodoxe et
efficace.
Bruno Desthuilliers
Le #16776771
Christophe a écrit :
Bruno Desthuilliers a écrit :
Christophe a écrit :
pil91 a écrit :
Bonjour, j'ai besoin de transformer en format csv délimité, des
fichiers fixes.




(snip)

C'est pas une suggestion d'optim (enfin, ça pourrait le devenir) mais
y a-t-il une raison pour laquelle vous n'utilisez pas le module
standard csv ?



Peut-être le fait que le format csv soit la cible, pas la source ?-)



Et ? Le module csv fait lecture et écriture.



Oui. Il lit effectivement des csv. Ce qui n'est pas le cas des fichiers
sources de l'OP, ni la source de son problème.



Help on module csv:

NAME
csv - CSV parsing and writing.

FILE
c:python25libcsv.py

DESCRIPTION
This module provides classes that assist in the reading and writing
of Comma Separated Value (CSV) files, and implements the interface
(...)



Merci, c'est gentil, je ne l'utilise qu'une cinquantaine de fois par an,
ce module...
Bruno Desthuilliers
Le #16777061
pil91 a écrit :
(snip)
Effectivement l'utilisation du module csv serait plus orthodoxe et
efficace.



Pour l'écriture, éventuellement, et encore seulement pour des trucs un
peu complexes (quotes etc). Pour la lecture, je doute... Si j'ai bien
compris, chaque ligne de ton fichier source est composé de X champs,
chaque champ ayant une taille (fixe) differente. Si oui, ça n'a aucun
rapport avec un format csv, donc...
Publicité
Poster une réponse
Anonyme