Autrement dit, le remplacement du deuxième int du fichier (offsets 4 à 8) n'a pas du tout lieu, et la relecture suivante me donne n'importe quoi (quoi que la dernière écriture du troisième int (offsets 8 à 12) a apparament marché malgré tout)
Dans mon vrai programme, il est important que je puisse à la fois lire et écrire dans mon fichier, d'où le mode r+. IL semblerait que ce mode soit passablement buggé...
OS : windows XP 32 bits
Compilateur : MinGW 3.4.5
J'ai pas essayé sous linux, mais je suis sûr que le pire, c'est que ça marche (hormis les Sleep bien sûr, mais de toute façon ils sont essentiellement là pour faire beau)
Tes fread et fwrite ne sont pas bons :
le 2e et 3e argument, c'est d'abord la taille, puis le nombre.
Donc :
int re = fread(&x, sizeof(x),1, fp);
Essaie. (et pareil pour fwrite)
Ce n'est pas censé avoir une incidence n'est-ce pas ? fread et fwrite n'ont pas à savoir si j'écris 4 chars ou 1 int, la seule chose qui leur importe c'est que je vais écrire 4 octets. Ce sont biens des buffers sous forme de void*, le type exact n'importe donc pas.
L'avantage de faire ce que j'ai fait, c'est qu'en retour j'obtiens le nombre d'octets lus ou écrits... en inversant les paramètres, j'obtiens toujours 1, ce qui n'a pas une grande signification (disons que connaître le nombre exact d'octets écrits peut avoir une utilité au-delà de savoir si succès/échec)
J'ai tout de même essayé, mais comme on pouvait s'y attendre, ça ne change strictement rien. Ca a juste changé la valeur autour de 4000000 qui semblait de toute façon tomber de nulle part. A noter que quand j'exécute le programme avec gdb, cette valeur apparament aléatoire change aussi.
Pour l'incidence que ça peut avoir, est ce que ça ne peut pas tomber sous le coup de l'endianness.
Je pense que non, mais j'ai toujours un doute.
Bon, tu dis que tu as essayé et que ça ne change pas.
Pour ma part, je te dirais que je ne fais jamais de "r+", soit j'écris, soit je lis. Je n'aime pas mélanger. Est ce que le soucis pourrait venir de la ? Est ce que "w+" fait la meme chose ?
J'hésitais à poser la question, mais comme Fvirtman, un fichier ouvert en lecture, écriture ne m'inspire pas confiance...
Ca pose pas mal de problèmes.
Pour l'incidence que ça peut avoir, est ce que ça ne peut pas tomber sous le coup de l'endianness. Je pense que non, mais j'ai toujours un doute.
JE ne vois pas ce que l'endianess vient faire ici. J'écris des int et je les relis juste après, je suis toujours sur la même machine donc logiquement, non. En plus cette valeur bizarre n'a strictement rien à voir avec le nombre 11 et ce n'est pas la même dans gdb ou en dehors. Rien ne va dans ce sens...
Citation
Je n'aime pas mélanger. Est ce que le soucis pourrait venir de la
? Est ce que "w+" fait la meme chose ?
LE problème de w+, c'est qu'il écrase quand même le contenu à l'ouverture. En fait ma stratégie à la base c'est que j'ouvre le fichier en R+, et si ça foire je l'ouvre en W+ (parce que r+ échoue si le fichier n'existe pas déjà).
JE vais malgré tout faire un autre test, juste pour voir.
Citation
Tu peux préciser pourquoi, tu dois faire ça?
JE suis en train de m'amuser à écrire un éditeur de fichier binaires en ligne de commande, si possible multiplateforme. Je sais, c'est ni très original ni très utile.
JE pensais que c'était mieux de faire ainsi plutôt que d'agir sur des buffer comme on le fait habituellement, au cas où on souhaiterait éditer de gros fichiers. Mais bon, si ça ne marche pas, j'en reviendrai à cette solution qui est au moins sûre de marcher.
Si on remplace le read par un ftell pour venir se placer au bon endroit, ça fonctionne.
Par contre l'enchainement fread, fwrite, dans un fichier r+, ne semble pas fonctionner sous Windows.
Pour les modes d'ouverture bidirectionnels (r+, a+, w+...) une lecture suivie d'une écriture représente un comportement indéfini (selon la norme).
Il faut toujours flusher le fichier entre deux opérations différentes (écriture suivie de lecture ou lecture suivie d'écriture. Sauf une lecture qui a résulté à un EOF). Pour flusher le fichier la norme préscrit l'une des fonctions : fflush, fseek ou rewind.
Donc revoies ton code.
Et au lieu de fermer le fichier pour le ré-ouvrir derrière, il existe freopen. Et 1 seconde n'est peut être pas suffisante, bref c'est code qui peut avoir un comportement hasardeux selon si l'OS a eu le temps de fermer le flux avant l'écoulement d'une seconde ou pas.
edit:
d'ailleurs, ça provoque une erreur au write suivant le fflush.
En revanche ça fonctionne avec fread suivi, d'un fseek, et d'un fwrite.
Pourtant, si je fais un ftell après le read, je trouve bien la taille d'un entier, pourtant le fwrite suivant ne fonctionne pas.
Oui pour moi aussi, il se peut qu'on ait trop demandé à l'OS ?!
fflush pousse l'OS à mettre le fichier à jour dans le DD, sans même parler des buffers ouverts en RAM, et généralement ce genre de requêtes ne sont pas prioritaires pour l'OS donc il prend son temps pour le faire.
Je me demande dans quels cas ce genre de pratiques seraient justifiées ? (je pense dans aucun cas).
Pour les modes d'ouverture bidirectionnels (r+, a+, w+...) une lecture suivie d'une écriture représente un comportement indéfini (selon la norme).
Il faut toujours flusher le fichier entre deux opérations différentes (écriture suivie de lecture ou lecture suivie d'écriture. Sauf une lecture qui a
résulté à un EOF). Pour flusher le fichier la norme préscrit l'une des fonctions : fflush, fseek ou rewind.
Les flush après chaque changement de direction ne règlent pas le problème.
* Si on est en mode lecture et qu'on fait un flush, ça fait planter les écritures suivantes (dans mon code ça affiche write error). Dans un certain sens c'est logique, on n'a pas à flusher un flux de lecture, c'est un comportement indéfini.
* Si on ne fait pas de flush après les read et avant les write, alors les write ne fonctionnent pas, bien qu'il n'y ait aucune erreur.
Flush mène donc à une impasse. Si par contre je remplace chaque flush par fseek(fp, 0, SEEK_CUR), ça marche... mais je ne comprends absolument pas pourquoi. En principe, c'est une action inutile, puisque ça équivaut à sauter sur place.
Citation
Et au lieu de fermer le fichier pour le ré-ouvrir derrière, il existe freopen. Et 1 seconde n'est peut être pas suffisante, bref c'est code qui peut avoir
un comportement hasardeux selon si l'OS a eu le temps de fermer le flux avant l'écoulement d'une seconde ou pas.
Même si on met des sleep plus longs, ça ne change rien, j'ai aussi essayé.
En ce qui concerne freopen, c'est voulu que je ne l'utilise pas: dans mon cas réel avec mon vrai fichier, c'est justement entre deux lancements du programme, et donc forcément en plusieurs ouvertures successives, que je constate des données erronnées.
Je viens de découvrir un autre fait intéressant avec mon exemple : si je ne supprime pas le fichier à la fin, en l'ouvrant dans un éditeur hexadécimal, je vois bien les octets 4 à 8 qui contiennent n'importe quoi, même si je rouvre le fichier plusieurs minutes plus tard.
When a file is opened with update mode ('+' as the second or third character in the
above list of mode argument values), both input and output may be performed on the
associated stream. However, output shall not be directly followed by input without an
intervening call to the fflush function or to a file positioning function (fseek,
fsetpos, or rewind), and input shall not be directly followed by output without an
intervening call to a file positioning function, unless the input operation encounters endof-
file. Opening (or creating) a text file with update mode may instead open (or create) a
binary stream in some implementations.
Pour ce que tu as trouvé dans le fichier ne serait-ce pas EOF ?
@GurneyH: tu es au moins le troisième qui m'envoie sur usenet, malheureusement je n'ai aucune idée de comment ça fonctionne au juste. Je ne crois pas que j'aie de programme pour lire ça, et j'ai pas l'impression qu'on peut s'inscrire et poster directement sur le web comme dans un forum normal. Dommmage, ça a l'air d'être de sacrées pointures là-bas...
Je me trompe peut-être mais avez-vous essayer en ouvrant trois fois le fichier en mode binaire ? Ce problème m'a fait penser à ce passage du livre "Méthodologie de la programmation en C" :
Citation : Méthodologie de la programmation en C p 267
De manière générale il existe deux types de flots :
- Les flots texte dont le contenu est découpé en lignes séparées par le catactère '\n'. Le contenu du flot peut subir des modifications selon les conventions de représentation de texte de l'environnement, comme par exemple la suppression des espaces précédant un caractère '\n'.
- Les flots binaires qui permettent de transférer sans altération le codage interne des données.
Sur certains systèmes, les flots texte peuvent être restreints de sorte qu'ils peuvent seulement transmettre des caractères affichables et certains caractères de contrôle. Par contre, les flots binaires ne comportent aucune restriction.
Cette distinction entre flots texte et flots binaires et imposée par la compatibilité avec des systèmes dans lesquels les applications orientées textes utilisent un format de fichier spécifique. Elle n'a pas de sens sous un système UNIX ou un système compatible POSIX, tous les fichiers étant dans ce cas gérés de façon homogène.
Mais Quentin C 2 précisait bien le mode d'ouverture binaire.
Oui, c'est très important.
Sous linux, ça ne change pas grand chose.
Par contre sous windows, le mode texte fait que chaque \n seul est remplacé automatiquement par \r\n. Écrire des données binaires a donc toutes les chances de foirer de façon complètement inattendue si on ne le précise pas explicitement.
La réponse m'a été donnée. Il semble que ce soit un bug dans l'implémentation de stdio.h par Microsoft.
(au moins dans le sens lecture, suivi d'une écriture, comme dans le code que j'ai présenté).
La solution est de placer
fseek(fp, 0, SEEK_CUR);
entre 2 opérations avec le mode "rb+"
J'ai testé avec ton code du premier post, ça fonctionne.
Voir la réponse sur fr.comp.lang.c, pour les détails.
Mais Quentin C 2 précisait bien le mode d'ouverture binaire.
Au temps pour moi
Merci pour la question posée sur fr.comp.lang.c GurneyH, mais j'ai une question suite à cette réponse: uknow a posté ce passage de la Norme C99 :
Citation : Norme C99 7.19.5.3 p 218
When a file is opened with update mode ('+' as the second or third character in the
above list of mode argument values), both input and output may be performed on the
associated stream. However, output shall not be directly followed by input without an
intervening call to the fflush function or to a file positioning function (fseek,
fsetpos, or rewind), and input shall not be directly followed by output without an
intervening call to a file positioning function, unless the input operation encounters end-
of-file. Opening (or creating) a text file with update mode may instead open (or create) a
binary stream in some implementations.
Or si je lis bien, celle-ci dit explicitement que si l'on ouvre un fichier en "update mode"(avec un "+"), les opérations d'écriture ne doivent pas suivre les opérations de lecture sans un appel à ftellfsetpos, fseek ou rewind(sauf si la lecture atteint la fin du fichier). Si c'est bien juste, Windows respecte bel et bien la Norme. Me trompai-je ?
Je pense que c'est conforme à la norme, l'erreur que nous avons faite c'est d'avoir utilisé fflush sur un flux d'"update" dont la dernière opération était une lecture, ce qui est énoncé parmi les cas indéfinis dans l'annexe J2 - Undefined behavior. Et la réponse apportée par GurneyH ne fait que confirmer le passage 7.19.5.3 -(6.
Merci Uknow, GurneyH et taurre. La solution est donc de faire fseek(fp, 0, SEEK_SET) à chaque fois qu'on change de mode, pour être sûr de ne rien laisser passer.
Entre temps j'ai attentivement fouillé les arcanes de stdio.h et je suis tombé sur la fonction setbuf. Elle a l'air d'être standard et résoud apparament également le problème initial. En ajoutant setbuf(fp,NULL) juste après l'ouverture du fichier, ça désactive le buffering.
Qu'en pensez-vous ? S'agit-il vraiment d'une fonction standard et non pas propre à la MSVCRT (comment savoir) ? Est-ce que cette solution marche aussi ailleurs que sous windows ?
Qu'en pensez-vous ? S'agit-il vraiment d'une fonction standard et non pas propre à la MSVCRT (comment savoir) ? Est-ce que cette solution marche aussi ailleurs que sous windows ?
C'est une fonction standard (d'après la C99), donc tu as la garantie qu'elle agira de la même façon énoncée dans sa page man quelque soit l'implémentation et l'environnement.
Pour ce qui est de son rapport avec le problème initial, j'ai tendance à dire que si la norme explicite qu'il faut utiliser une fonction de positionnement dans un cas identique à celui qu'on a là sans citer d'exceptions, alors il n'y a pas d'autres façons sûres de faire.
Or si je lis bien, celle-ci dit explicitement que si l'on ouvre un fichier en "update mode"(avec un "+" ), les opérations d'écriture ne doivent pas suivre les opérations de lecture sans un appel à ftell fsetpos, fseek ou rewind(sauf si la lecture atteint la fin du fichier). Si c'est bien juste, Windows respecte bel et bien la Norme. Me trompai-je ?
Non, tu ne te trompes pas, c'est écrit noir sur blanc.
La personne qui m'a répondu sur comp lang.c avait un doute sur ce point.
Je pense tout de même que le mode lecture/écriture, est très peu utilisé.
D'ailleurs, il serait intéressant de trouver des sources "sérieux", qui l'utilise.
Je pense que c'est conforme à la norme, l'erreur que nous avons faite c'est d'avoir utilisé fflush sur un flux d'"update" dont la dernière opération était une lecture, ce qui est énoncé parmi les cas indéfinis dans l'annexe J2 - Undefined behavior. Et la réponse apportée par GurneyH ne fait que confirmer le passage 7.19.5.3 -(6.
Citation : GurneyH
Non, tu ne te trompes pas, c'est écrit noir sur blanc.
Merci pour la confirmation
Citation : QuentinC 2
Entre temps j'ai attentivement fouillé les arcanes de stdio.h et je suis tombé sur la fonction setbuf. Elle a l'air d'être standard et résoud apparament également le problème initial. En ajoutant setbuf(fp,NULL) juste après l'ouverture du fichier, ça désactive le buffering.
Qu'en pensez-vous ? S'agit-il vraiment d'une fonction standard et non pas propre à la MSVCRT (comment savoir) ? Est-ce que cette solution marche aussi ailleurs que sous windows ?
Les fonctions setbuf et setvbuf sont bel et bien des fonctions standard(depuis le C89). Quant à leur impact sur le problème j'ai, comme uknow, un doute. Le fait que l'on doive faire appel à fseek, fsetpos ou rewind entre une lecture/écriture ne me semble pas lié au tampon puisque ces fonctions n'ont aucune influence dessus. De plus, le fait de ne pas utilisé de tampon risque de ralentir les opérations de lecture/écriture étant donné que tu va multiplier les appels système.
EDIT :
Citation : QuentinC 2
(comment savoir)
Pour savoir si une fonction est standard ou pas il te suffit de regarder dans la Norme C99.
Concernant la Norme C89 je ne connais pas de liens gratuit pour en disposer. En ce qui me concerne je me réfère au K&R. Sinon, il y a aussi le sujet de rz0 listant les fonctions standard du C89.
le fait de ne pas utilisé de tampon risque de ralentir les opérations de lecture/écriture étant donné que tu va multiplier les
appels système.
Ca c'est certain, c'est même pas un risque, à mon avis c'est 100% exact.
Mais de toute façon le buffer est obligé de se vider quand on fait fseek, donc ça ne doit en fait pas changer grand chose au niveau des performances, qui seront de toute façon relativement catastrophiques par rapport à une lecture ou une écriture classique.
Merci pour toutes vos réponses, vos points de vue ont tous étés éclairants. JE pense qu'on a fait le tour du problème cette fois-ci, je considère donc ce sujet comme résolu.
Citation
Je pense tout de même que le mode lecture/écriture, est très peu utilisé.
D'ailleurs, il serait intéressant de trouver des sources "sérieux", qui l'utilise.
J'avoue que je serais aussi curieux de savoir. Si quelqu'un sait des choses là-dessus, qu'il n'hésite pas à poster même si ce sujet est résolu.
P.S. Je garde une copie de la norme, ça pourra toujours servir, on ne sait jamais.
Panique dans la manipulation des fichiers
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Objectif Zéro Bug - le test logiciel professionnel | L'électronique de zéro | Tableaux & pointeurs | Pointeurs sur fonctions | Lecture/écriture binaire
Objectif Zéro Bug - le test logiciel professionnel | L'électronique de zéro | Tableaux & pointeurs | Pointeurs sur fonctions | Lecture/écriture binaire
Objectif Zéro Bug - le test logiciel professionnel | L'électronique de zéro | Tableaux & pointeurs | Pointeurs sur fonctions | Lecture/écriture binaire
Objectif Zéro Bug - le test logiciel professionnel | L'électronique de zéro | Tableaux & pointeurs | Pointeurs sur fonctions | Lecture/écriture binaire
Objectif Zéro Bug - le test logiciel professionnel | L'électronique de zéro | Tableaux & pointeurs | Pointeurs sur fonctions | Lecture/écriture binaire