[Python] Petit soucis d'écriture de fichier
Fermé
Julie
-
28 mars 2010 à 15:48
Char Snipeur Messages postés 9813 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 - 15 avril 2010 à 08:44
Char Snipeur Messages postés 9813 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 - 15 avril 2010 à 08:44
A voir également:
- [Python] Petit soucis d'écriture de fichier
- Fichier rar - Guide
- Fichier host - Guide
- Comment ouvrir un fichier epub ? - Guide
- Comment réduire la taille d'un fichier - Guide
- Écriture facebook - Guide
4 réponses
heyquem
Messages postés
759
Date d'inscription
mercredi 17 juin 2009
Statut
Membre
Dernière intervention
29 décembre 2013
131
Modifié par heyquem le 14/04/2010 à 12:59
Modifié par heyquem le 14/04/2010 à 12:59
Ouh lala.
Il y a en effet un histoire de pointeur, mais il faut en expliquer un peu plus pour faire vraiment comprendre.
Python est implémenté en langage C et utilise donc la fonction fopen de C pour ouvrir les fichiers, que l'on trouve décrite ici:
https://www.microsoft.com/en-us/download/details.aspx?id=55979
fopen renvoie un pointeur qui pointe vers le fichier ouvert.
Ce pointeur (ou plus exactement la position vers laquelle il pointe) se déplace dans le fichier au gré de l'activation des fonctions Python read(), readline() et seek().
fichier.read(34) lit 34 caractères vers l'avant et le pointeur se déplace d'autant.
fichier.seek(-78,2) déplace le pointeur de 78 caractères vers l'arrière à partir de la position courante.
fichier.readline() , quelle que soit la position courante, lit la ligne jusqu'au prochain caractère de fin de ligne et positionne le pointeur de fichier après lui, c'est à dire devant le premier caractère de la ligne suivante.
Quel que soit le mode d'ouverture du fichier (r,w,a,r+,w+,a+) , juste après l'ouverture, le pointeur est positionné sur le début du fichier (il suffit de faire print f.tell() pour le constater).
Voici donc ce qui se passe dans le cas de ton code, Julie:
- ouverture de 'fichier.txt' en mode 'r+'. Le pointeur se trouve à la position 0.
- ouverture de 'fiche.txt' en mode 'a'. Le pointeur se trouve à la position 0.
- un objet de type 'entier' codant la valeur 1011 est créé et une référence (une étiquette) po lui est attachée
- évaluation de la condition (fichier.readline()!= "") : pour ce faire il y a lecture de la premiêre ligne -> le pointeur de 'fichier.txt' se retrouve après cette lecture positionné devant le premier caractère de la deuxième ligne
- l'instruction fiche.write(fichier.readline()+"\n") est exécutée :
elle comporte d'abord une lecture de ligne, c'est donc la deuxième ligne qui est lue. Suite à cette lecture, le pointeur du fichier 'fichier.txt' se retrouve positionné devant le premier caractère de la 3ième ligne.
- puis cette ligne qui vient d'être lue (la deuxième de 'fichier.txt' ) est écrite dans le second fichier 'fiche.txt'. Ce fichier ayant été ouvert en mode 'a', le pointeur de ce fichier, qui était jusqu'à ce moment là positionné à 0, est automatiquement amené à la fin du fichier préalablement à toute écriture: le mode 'a' est un mode de protection des données initiales contenues dans le fichier. L'écriture de la ligne provenant de 'fichier.txt' est alors effectuée à la fin de 'fiche.txt'. Après cette écriture, le pointeur de 'fiche.txt' se retrouve positionné après la nouvelle fin du fichier, c'est à dire après ce qui vient d'être écrit. Il suffit de faire print f.tell() pour le constater.
- il faut noter que readline() lit une ligne jusque et Y COMPRIS la fin de ligne. Si on fait fiche.write(fichier.readline()), on obtient donc l'écriture d'une ligne tout à fait bien constituée, c'est à dire avec une fin de ligne au bout. Il n'est donc pas nécessaire d'ajouter + '\n' à la suite de la ligne lue que l'on veut transférer. Si on écrit fiche.write(fichier.readline() + '\n'), il en résulte simplement que le contenu de la ligne transféré se retrouve suivi dans le fichier 'fiche.txt' , après écriture, de DEUX fins de ligne. À la lecture de ce fichier, on verra alors à l'écran, une ligne blanche suivant la ligne transférée.
- la valeur représentée par l'objet référencé par po est affichée
- un objet de type 'entier' codant la valeur 1011-1 est créé, le nom po est décollé de l'objet codant 1011 et attaché au nouvel objet codant 1010 pour servir de référence de cet objet.
- un nouveau tour de la boucle while commence: évaluation de la condition (fichier.readline()!= "") : pour ce faire c'est la ligne au début de laquelle se trouve le pointeur de 'fichier.txt' qui est lue; donc la 3ième ligne. Le pointeur de ce fichier se retrouve après cette lecture en tête de la 4ième ligne.
- fiche.write(fichier.readline() + '\n') lit la 4ième ligne de 'fichier.txt', le pointeur se retrouve alors au début de la 5ième ligne, puis le contenu de la ligne(le texte affichable) et deux '\n' qui le suivent sont écrits dans 'fiche.txt', à la fin de ce dernier puisque son pointeur y était, mais de toutes façons dans un fichier ouvert en 'a' il ne peut être écrit ailleurs qu'à sa fin
- et ça continue ainsi , en faisant passer le nom po d'objet en objet ( ces objets successifs codant pour une valeur sans cesse décroissante jusqu'à 0 ), et en lisant toutes les lignes de 'fichier.txt' mais seule une sur deux ést effectivement transférée dans 'fiche.txt'.
L'ajout de '\n' à la fin d'une ligne lue, avant son écriture dans 'fiche.txt', donne à l'affichage une ligne blanche, créant la surpenante illusion que seule une ligne sur deux transférées est recopiée correctement dans le nouveau fichier. En fait ce n'est pas un problème de recopiage de toutes les lignes, mal effectué pour une ligne sur deux, mais de recopiage qui n'est effectué que pour une ligne sur deux.
----------------------------------------------------------
Il faut noter que si on est sous Windows et que l'on utilise les fonctions seek() pour déplacer le pointeur d'un fichier avant des opérations de lecture ou d'écriture, ou tell() pour connaître sa position, sur un fichier dont on connaît le contenu, on constatera des écarts entre les positions attendues et celles renvoyées par tell(). Sous Windows, il y a en effet un fonctionnement particulier, que je pourrai préciser si cela est demandé., mais je ne veux pas en rajouter pour le moment.
---------------------------------------------------------
6ril, ne te rends tu pas compte de ce que tu fais faire par ton code ?
text1 = fichier1.readlines() crée une liste de lignes.
Soit dit en passant readlines() conserve les fins de ligne. Les éléments de text1 sont donc les lignes de fichier 1 avec leurs fins de ligne.
Et ensuite, quand il s'agit d'écrire dans fichier 2, tu fais faire:
''.join(text1) c'est à die que tu recrées une chaîne par concaténation des lignes de la liste text1. Comme ces lignes comportent les fins de ligne, on obtient bien la même chaîne que celle dont on était parti, c'est à dire la chaîne qui constitue le fichier avant qu'elle soit découpée par readlines() en morceaux se terminant par des fins de ligne.
Découper le texte en lignes puis recoller les lignes est i-nu-ti-le.
Je suis frappé de voir que personne ne semble se rendre compte qu'un fichier n'est qu'une succession linéaire de caractères, une chaîne tout simplement. Un fichier ne connaît pas la notion de lignes, il ne fait que comporter des caractères '\n' à certains endroits de la chaîne et c'est simplement l'interprétation de ces caractères par le moteur de rendu au moment d'un affichage qui fait apparaître au yeux d'un lecteur un effet qui est appelé 'retour à la ligne', mais il n'y a pas de ligne dans le fichier.
Aussi pour transférer un fichier suffit il de faire (mis à part d'autres types de problèmes):
./
---------------------------------------------------------------
Char Snipeur,
Ton code ne semble pas marcher. Tout ce que j'obtiens en le faisant tourner, c'est
NameError: name 'tmp' is not defined
Et si je définis tmp avant d'entrer dans la boucle, c'est sa valeur qui s'affiche, ou est transférée.
Ta valeur tampon n'est rien d'autre qu'une ligne lue.
Je ne vois qu'une façon d'écrire le code que veut Julie:
po = 1011 #nombre de lignes du fichier 1
Puisqu'on connaît po au départ, il est d'ailleurs préférable d'écrire:
(La ligne 0 est la première, etc..)
Mais il y a mieux, car un tel code nécessite de connaître à l'avance le nombre de lignes dans le fichier (en fait le nombre de '\n' présents dans la chaîne qui constitue le fichier):
Ce code permet de progresser dans le fichier lu ligne par ligne, et donc de faire éventuellement des tests sur chaque ligne.
Mais en absence d'un tel besoin, le transfert du contenu du fichier sous forme de chaîne est encore le plus simple:
Il y a en effet un histoire de pointeur, mais il faut en expliquer un peu plus pour faire vraiment comprendre.
Python est implémenté en langage C et utilise donc la fonction fopen de C pour ouvrir les fichiers, que l'on trouve décrite ici:
https://www.microsoft.com/en-us/download/details.aspx?id=55979
fopen renvoie un pointeur qui pointe vers le fichier ouvert.
Ce pointeur (ou plus exactement la position vers laquelle il pointe) se déplace dans le fichier au gré de l'activation des fonctions Python read(), readline() et seek().
fichier.read(34) lit 34 caractères vers l'avant et le pointeur se déplace d'autant.
fichier.seek(-78,2) déplace le pointeur de 78 caractères vers l'arrière à partir de la position courante.
fichier.readline() , quelle que soit la position courante, lit la ligne jusqu'au prochain caractère de fin de ligne et positionne le pointeur de fichier après lui, c'est à dire devant le premier caractère de la ligne suivante.
Quel que soit le mode d'ouverture du fichier (r,w,a,r+,w+,a+) , juste après l'ouverture, le pointeur est positionné sur le début du fichier (il suffit de faire print f.tell() pour le constater).
Voici donc ce qui se passe dans le cas de ton code, Julie:
- ouverture de 'fichier.txt' en mode 'r+'. Le pointeur se trouve à la position 0.
- ouverture de 'fiche.txt' en mode 'a'. Le pointeur se trouve à la position 0.
- un objet de type 'entier' codant la valeur 1011 est créé et une référence (une étiquette) po lui est attachée
- évaluation de la condition (fichier.readline()!= "") : pour ce faire il y a lecture de la premiêre ligne -> le pointeur de 'fichier.txt' se retrouve après cette lecture positionné devant le premier caractère de la deuxième ligne
- l'instruction fiche.write(fichier.readline()+"\n") est exécutée :
elle comporte d'abord une lecture de ligne, c'est donc la deuxième ligne qui est lue. Suite à cette lecture, le pointeur du fichier 'fichier.txt' se retrouve positionné devant le premier caractère de la 3ième ligne.
- puis cette ligne qui vient d'être lue (la deuxième de 'fichier.txt' ) est écrite dans le second fichier 'fiche.txt'. Ce fichier ayant été ouvert en mode 'a', le pointeur de ce fichier, qui était jusqu'à ce moment là positionné à 0, est automatiquement amené à la fin du fichier préalablement à toute écriture: le mode 'a' est un mode de protection des données initiales contenues dans le fichier. L'écriture de la ligne provenant de 'fichier.txt' est alors effectuée à la fin de 'fiche.txt'. Après cette écriture, le pointeur de 'fiche.txt' se retrouve positionné après la nouvelle fin du fichier, c'est à dire après ce qui vient d'être écrit. Il suffit de faire print f.tell() pour le constater.
- il faut noter que readline() lit une ligne jusque et Y COMPRIS la fin de ligne. Si on fait fiche.write(fichier.readline()), on obtient donc l'écriture d'une ligne tout à fait bien constituée, c'est à dire avec une fin de ligne au bout. Il n'est donc pas nécessaire d'ajouter + '\n' à la suite de la ligne lue que l'on veut transférer. Si on écrit fiche.write(fichier.readline() + '\n'), il en résulte simplement que le contenu de la ligne transféré se retrouve suivi dans le fichier 'fiche.txt' , après écriture, de DEUX fins de ligne. À la lecture de ce fichier, on verra alors à l'écran, une ligne blanche suivant la ligne transférée.
- la valeur représentée par l'objet référencé par po est affichée
- un objet de type 'entier' codant la valeur 1011-1 est créé, le nom po est décollé de l'objet codant 1011 et attaché au nouvel objet codant 1010 pour servir de référence de cet objet.
- un nouveau tour de la boucle while commence: évaluation de la condition (fichier.readline()!= "") : pour ce faire c'est la ligne au début de laquelle se trouve le pointeur de 'fichier.txt' qui est lue; donc la 3ième ligne. Le pointeur de ce fichier se retrouve après cette lecture en tête de la 4ième ligne.
- fiche.write(fichier.readline() + '\n') lit la 4ième ligne de 'fichier.txt', le pointeur se retrouve alors au début de la 5ième ligne, puis le contenu de la ligne(le texte affichable) et deux '\n' qui le suivent sont écrits dans 'fiche.txt', à la fin de ce dernier puisque son pointeur y était, mais de toutes façons dans un fichier ouvert en 'a' il ne peut être écrit ailleurs qu'à sa fin
- et ça continue ainsi , en faisant passer le nom po d'objet en objet ( ces objets successifs codant pour une valeur sans cesse décroissante jusqu'à 0 ), et en lisant toutes les lignes de 'fichier.txt' mais seule une sur deux ést effectivement transférée dans 'fiche.txt'.
L'ajout de '\n' à la fin d'une ligne lue, avant son écriture dans 'fiche.txt', donne à l'affichage une ligne blanche, créant la surpenante illusion que seule une ligne sur deux transférées est recopiée correctement dans le nouveau fichier. En fait ce n'est pas un problème de recopiage de toutes les lignes, mal effectué pour une ligne sur deux, mais de recopiage qui n'est effectué que pour une ligne sur deux.
----------------------------------------------------------
Il faut noter que si on est sous Windows et que l'on utilise les fonctions seek() pour déplacer le pointeur d'un fichier avant des opérations de lecture ou d'écriture, ou tell() pour connaître sa position, sur un fichier dont on connaît le contenu, on constatera des écarts entre les positions attendues et celles renvoyées par tell(). Sous Windows, il y a en effet un fonctionnement particulier, que je pourrai préciser si cela est demandé., mais je ne veux pas en rajouter pour le moment.
---------------------------------------------------------
6ril, ne te rends tu pas compte de ce que tu fais faire par ton code ?
text1 = fichier1.readlines() crée une liste de lignes.
Soit dit en passant readlines() conserve les fins de ligne. Les éléments de text1 sont donc les lignes de fichier 1 avec leurs fins de ligne.
Et ensuite, quand il s'agit d'écrire dans fichier 2, tu fais faire:
''.join(text1) c'est à die que tu recrées une chaîne par concaténation des lignes de la liste text1. Comme ces lignes comportent les fins de ligne, on obtient bien la même chaîne que celle dont on était parti, c'est à dire la chaîne qui constitue le fichier avant qu'elle soit découpée par readlines() en morceaux se terminant par des fins de ligne.
Découper le texte en lignes puis recoller les lignes est i-nu-ti-le.
Je suis frappé de voir que personne ne semble se rendre compte qu'un fichier n'est qu'une succession linéaire de caractères, une chaîne tout simplement. Un fichier ne connaît pas la notion de lignes, il ne fait que comporter des caractères '\n' à certains endroits de la chaîne et c'est simplement l'interprétation de ces caractères par le moteur de rendu au moment d'un affichage qui fait apparaître au yeux d'un lecteur un effet qui est appelé 'retour à la ligne', mais il n'y a pas de ligne dans le fichier.
Aussi pour transférer un fichier suffit il de faire (mis à part d'autres types de problèmes):
fichier1 = open ('fichier1', 'r') ch = fichier1.read() fichier2.write(ch)
./
---------------------------------------------------------------
Char Snipeur,
Ton code ne semble pas marcher. Tout ce que j'obtiens en le faisant tourner, c'est
NameError: name 'tmp' is not defined
Et si je définis tmp avant d'entrer dans la boucle, c'est sa valeur qui s'affiche, ou est transférée.
Ta valeur tampon n'est rien d'autre qu'une ligne lue.
Je ne vois qu'une façon d'écrire le code que veut Julie:
po = 1011 #nombre de lignes du fichier 1
while po !=0: line = fichier.readline() if line!= "": fiche.write(line) print po po -=1
Puisqu'on connaît po au départ, il est d'ailleurs préférable d'écrire:
for po in xrange(1011): line = fichier.readline() if line!= "": fiche.write(line) print po
(La ligne 0 est la première, etc..)
Mais il y a mieux, car un tel code nécessite de connaître à l'avance le nombre de lignes dans le fichier (en fait le nombre de '\n' présents dans la chaîne qui constitue le fichier):
line = fichier.readline() po = 0 while line: fiche.write(line) print po po,line = po+1,f.readline()
Ce code permet de progresser dans le fichier lu ligne par ligne, et donc de faire éventuellement des tests sur chaque ligne.
Mais en absence d'un tel besoin, le transfert du contenu du fichier sous forme de chaîne est encore le plus simple:
fichier1 = open ('fichier1', 'r') ch = fichier1.read() fichier2.write(ch)
pourquoi tu ne fais pas le travail en 2 étapes?
fichier1 = open ('fichier1', 'r')
text1 = fichier1.readlines()
fichier1.close()
fichier2 = open ('fichier2', 'w')
fichier2.write(''.join(text1))
fichier2.close()
ça me parait plus logique et je pense plus rapide (mais demande un peu plus de mémoire) à condition que ton fichier ne soit pas trop gros
fichier1 = open ('fichier1', 'r')
text1 = fichier1.readlines()
fichier1.close()
fichier2 = open ('fichier2', 'w')
fichier2.write(''.join(text1))
fichier2.close()
ça me parait plus logique et je pense plus rapide (mais demande un peu plus de mémoire) à condition que ton fichier ne soit pas trop gros
Char Snipeur
Messages postés
9813
Date d'inscription
vendredi 23 avril 2004
Statut
Contributeur
Dernière intervention
3 octobre 2023
1 298
12 avril 2010 à 14:12
12 avril 2010 à 14:12
C'est une histoire de pointeur, chaque appel à readline le déplace d'une ligne, il est donc tout à fait logique d'avoir la moitié de copier.
Il faut passer par une variable tampon.
Il faut passer par une variable tampon.
po = 1011 #nombre de lignes du fichier 1 while( po !=0): if ((tmp=fichier.readline())!= ""): fiche.write(tmp+"\n") print po po -=1 fiche.close() fichier.close()Je ne connais pas bien le python, mais ça devrais fonctionner.
heyquem
Messages postés
759
Date d'inscription
mercredi 17 juin 2009
Statut
Membre
Dernière intervention
29 décembre 2013
131
Modifié par heyquem le 14/04/2010 à 14:55
Modifié par heyquem le 14/04/2010 à 14:55
C'est que si le fichier est très gros on risque de saturer la rame.
Tout à fait. C'est bien parce que les cas de figures et les problèmes associés ne se limitent pas au cas simple envisagé que j'ai écrit
Aussi pour transférer un fichier suffit il de faire (MIS À PART D'AUTRES TYPES DE PROBLÈMES)
S'il s'agit d'un problème de saturation de la rame, il faut mettre d'autres rames et y mettre dessus des rameurs supplémentaires. ;|
En C++ il est possible de faire ça car "=" retourne une valeur.
OK. Je ne connais pas C++. C'est trop difficile pour moi.
Char Snipeur
Messages postés
9813
Date d'inscription
vendredi 23 avril 2004
Statut
Contributeur
Dernière intervention
3 octobre 2023
1 298
15 avril 2010 à 08:44
15 avril 2010 à 08:44
C++ n'est pas plus difficile, la logique est un peu différente.
Modifié par 6ril le 14/04/2010 à 13:28
en fait j'étais parti de son code et j'ai juste voulu dire que ne voulant pas modifié le fichier il n'étatit pas nécessaire de traiter ligne par ligne mais il est vrai que ton raisonnement va encore plus loin que moi à juste titre
en tout cas jolies démonstrations
14 avril 2010 à 14:11
Le problème avec le code suivant :
C'est que si le fichier est très gros on risque de saturer la rame.