lire écrire lecture écriture fichier texte txt mode r+ seek tell write writelines
update actualiser corriger actualisation correction
réécrire modifier modification
J'en ai eu marre de ne rien comprendre à la façon dont fonctionnent les modes de fichier r+ w+ a+ , et je me suis donc attelé à chercher des explications sur le net.
Ce n'était pas la première fois et j'ai de nouveau eu l'impression d'être comme Théodore Monod à la recherche de la météorite de Chinguetti: on ne trouve quasiment rien !
C'est le désert d'explications sur ce sujet pourtant très essentiel. Les explications qu'on trouve sont pour la plupart sommaires et toujours les mêmes.
Il se peut qu'il y ait des explications en bonne et due forme sur developpez.com, mais même si c'est le cas, le fait est que je ne les ai pas trouvées.
De même, dans le livre de Swinnen qui est tellement porté aux nues, une recherche de 'r+' conduit à UNE seule occurence de ces caractères dans tout le livre, et encore est-ce seulement au sein du code d'un exemple. Aucun chapitre spécifique ne détaille ce sujet dans ce livre, sauf erreur.
Dans "Plonger au cœur de Python" il n'y a rien non plus.
En recherchant dans les divers forums sur Internet je me suis souvent perdu dans la masse de références plus ou moins bien liées à ma question, et je n'ai en tous cas pas trouvé de réponses expliquant les choses avec clarté qui m'aient permis de comprendre le sujet.
Déprimant et énervant.
Mais cette fois ci, je me suis obstiné.
On lit ici ou là que
l'ouverture en modes 'r' et 'r+' positionne le pointeur au début du fichier,
tandis que l'ouverture en modes 'a' et 'a+' le positionne à la fin.
Je me suis fixé de d'abord comprendre comment marche ce foutu mode 'r+'. Est-ce qu'on ne peut écrire qu'au début du fichier ? Peut on écrire au milieu ?
Je suis retombé sur la page suivante,
http://jam-bazaar.blogspot.com/2007/...-r-w-mode.html
Je le connaissais déjà mais je n'en avais pas tiré profit parce que l'extrait suivant est ambigü:
« However, when you switch between reading and writing, there must be an intervening fflush, fsetpos, fseek, or rewind operation. The current position can be specified for the fsetpos or fseek operation, if desired. »
À cause du "or" et du "if desired" , on a l'impression que lorsqu'on veut passer de lecture à écriture, il suffit d'une seule des fonctions fflush, fsetpos, fseek, or rewind.
En plus ce sont des fonctions de C++, et le texte met ça en relation avec une erreur que commet Windows et que je n'ai jamais observée personnellement.
En outre, il ne précise pas ce qu'on peut lire dans la Python Library Reference à propos de fsync() dont je me suis oportunément souvenu (ça sert de se balader parfois dans le manuels):
« If you're starting with a Python file object f, first do f.flush(), and then do os.fsync(f.fileno()), to ensure that all internal buffers associated with f are written to disk. »
Il m'est soudain apparu qu'il était évident qu'il FAUT utiliser flush() pour écrire dans un fichier en mode 'r+' et que si ça n'avait pas marché quand j'avais fait des essais, même avec flush(), c'est parce que j'essayais d'écrire trop peu de caractères sans avoir utilisé fsync().
J'ai de nouveaux fait des essais avec flush() et fsync() mais ça marchait de façon erratique. Jusqu'à ce qu'à force de tatonnements, j'arrive à une compréhension qui est synthétisée dans le code suivant:
L'écriture n'est possible dans un fichier ouvert en 'r+' que dans deux cas
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 from os import fsync def execution(x,comm): print '---------------------------------------------------------------\n'+x+')'+comm # Creation de fichierY f = open('fichierY','w') f.write('France: Paris\nAllemagne: Berlin\nAutriche: Vienne\n') f.close() # Tentative d'ecriture en mode r+ f = open('fichierY','r+') if x=='1': pass if x=='2': f.readline() if x=='3': p = f.tell() if x=='4': f.readline() p = f.tell() if x=='5': f.seek(18) if x=='6': f.readline() f.seek(18) f.write(ch) f.flush() fsync(f.fileno()) f.close() # Visualisation du resultat f = open('fichierY','r') while 1: p,rd = f.tell(),f.readline() if not rd: break print 'p =',str(p+1000)[1:],rd[:-1] f.close() print 'Fichier de départ pour chaque essai : (deb = position du pointeur avant la lecture de la ligne)' f = open('fichierY','w') f.write('France: Paris\nAllemagne: Berlin\nAutriche: Vienne\n') f.close() f = open('fichierY','r') while 1: p,rd = f.tell(),f.readline() print 'deb =',str(p+1000)[1:], if not rd: break print rd[:-1] f.close() ch = raw_input('\n\nEntrer la chaine à sur-écrire dans ce fichier : ') print '\nEtats du fichier apres les traitements indiques' execution('1', ') sans lecture prealable') execution('2', ') f.readline()') execution('3', ') sans lecture prealable , p = f.tell()') execution('4', ') f.readline() p = f.tell()') execution('5', ') sans lecture prealable , f.seek(18)') execution('6', ') f.readline() f.seek(18)')
- soit juste après l'ouverture du fichier: le pointeur est alors à la position 0 et l'écriture se fait à partir du début du fichier.
- soit après avoir positionné le pointeur à un endroit quelconque au milieu du fichier au moyen de seek() .
Les choses sont rendues complexes par le fait que si on fait agir readline() sur le fichier à partir d'une situation où l'écriture est possible, c'est à dire juste après l'ouverture du fichier en 'r+ (cas de 'WEDU' dans exemple) ou après avoir fait seek(n) et write() ( cas de 'HHHHHHHHH' dans exemple), alors on perd la possibilité d'écrire; pour la restaurer, il faut exécuter à nouveau seek(n) une fois.
Par contre, une fois que la possibilité d'écrire est installée, on peut faire plusieurs write() à la suite
- même sans seek() : les écritures seront collées les unes à la suite des autres sans espace (cas de 'MMM" dans l'exemple)
- ou en sautant d'une position à une autre, en avant ou en arrière au sein du fichier avec seek() ( cas de '29' et '18' dans l'exemple)
Pour ce qui est de la lecture avec readline(), elle n'est pas remise en cause par des séquences d'écriture précédentes, on peut enchainer readline() juste après un write() (cas de la Ligne interne dans l'exemple).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73 def creation_de_xfichierY(): f = open('xfichierY','w') f.write('France: Paris\n') f.write('Allemagne: Berlin\n') f.write('Autriche: Vienne\n') f.close() def resultat(): print 'Resultat:\n' f = open('xfichierY','r') while 1: p,rd = f.tell(),f.readline() print 'p =',str(p+1000)[1:], if not rd: break print rd[:-1] f.close() from os import fsync print 'Fichier de départ: (deb = position du pointeur avant la lecture de la ligne)' creation_de_xfichierY() f = open('xfichierY','r') rd = 'go' while rd: p = f.tell() rd = f.readline() print 'deb =',str(p+1000)[1:],rd[:-1] f.close() print '============================================================' creation_de_xfichierY() f = open('xfichierY','r+') rd = f.readline() print 'Premiere ligne :',rd[0:-1] f.write('WEDU') f.flush() fsync(f.fileno()) f.seek(4) f.write('ZYX') f.flush() fsync(f.fileno()) f.write('MMM') f.flush() fsync(f.fileno()) f.seek(29) f.write('29') f.flush() fsync(f.fileno()) f.seek(18) f.write('18') f.flush() fsync(f.fileno()) rd = f.readline() print 'Ligne interne :',rd f.write('FOT') f.flush() fsync(f.fileno()) f.seek(36) f.write('HHHHHHHHH') f.flush() fsync(f.fileno()) resultat()
L'intérêt du mode 'r+' est tout de même limité par le fait que l'écriture procède par écrasement:
on n'écarte pas les bytes pour en glisser des supplémentaires au milieu, on écrase seulement le même nombre de caractères que celui qu'on écrit à partir du point de départ de l'écriture, comme le montre le code suivant. Il faut donc se méfier pour ne pas écrire un plus grand nombre de caractères que la longueur de la chaine qu'on veut corriger.
Dans ce mode 'r+', la taille du fichier ne change pas entre avant et après l'écriture.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 def creation_de_fichierY(): f = open('fichierY','w') f.write('Allemagne : Berlin - 3 400 000 habitants \nAutriche : Vienne - 1 600 000 habitants \n') f.write('Chili : Madrid - 5 500 000 habitants \nFrance : Paris - 11 300 000 habitants \n') f.write('Mexique : Tunis - 2 083 000 habitants \nPays Bas : Amsterdam - 739 000 habitants \n') f.close() def affichage_de_fichierY(x): print 'fichierY ' + x f = open('fichierY','r') rd = 'go' while rd: p,rd = f.tell(), f.readline() print 'p =',str(p+1000)[1:],rd[:-1] f.close() capitale = { 'Allemagne ':'Berlin','Autriche ':'Vienne','Chili ':'SANTIAGO DE CHILE',\ 'France ':'Paris','Mexique ':'MEXICO','Pays Bas ':'Amsterdam' } from os import fsync creation_de_fichierY() affichage_de_fichierY('avant traitement :') f = open('fichierY','r+') while 1: p,rd = f.tell(), f.readline()[:-1] if rd=='': break if rd[16:27] != capitale[rd[0:14]]: f.seek(p + 16) # <<<<================= f.write(capitale[rd[0:14]]) f.flush() fsync(f.fileno()) f.readline() f.close() affichage_de_fichierY('apres traitement :')
Pour résumer:
1) pour écrire dans un fichier ouvert en mode 'r+', il faut impérativement
- faire write() juste après l'ouverture du fichier ou juste après avoir utilisé seek() ou en recommançant un write() après un précédent
- il faut faire suivre l'instruction write() de flush() ET fsync().
2) le mode 'r+' est plus un mode de lecture-correction que de lecture-écriture.
Tout ceci est valable indifféremment pour une utilisation de write() ou de writelines()
--------------------------------------------------------
J'ai fait de mon mieux pour comprendre et vérifier tout ça. J'espère que je n'ai pas fait d'erreur ou de mauvaise compréhension.
Si tel était le cas, les tutos et manuels seraient bien inspirés de sortir l'accès aux bonnes explications de son ésotérisme actuel.
Si vous connaissez des pages donnant les explications que j'ai données ci-dessus, je suis intéressé de les connaître et de voir ce qui y est dit. S'il y en a, j'aurais bien aimé les connaître avant pour m'éviter les nombreux et fastidieux essais qu'il m'a fallu faire pour piger.
Partager