Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Lecture / écriture dans un fichier binaire

16 réponses
Avatar
GurneyH
Bonjour.

Je suis peu habitu=E9 =E0 manipuler des fichiers en lecture/=E9criture, et
je me trouve avec un code qui produit un r=E9sultat diff=E9rent suivant
qu'il est compil=E9 et ex=E9cut=E9 sous Ubuntu et GCC ou Windows et MingW.

#include <stdio.h>
#include <stdlib.h>

FILE *xfopen(char const *path, const char *mode)
{
FILE *fp =3D fopen(path, mode);
if(fp =3D=3D NULL)
{
perror("xfopen ");
exit(EXIT_FAILURE);
}
return fp;
}


int read_int(FILE *fp)
{
int ret;
if(fread(&ret, sizeof(int), 1, fp) < 1)
{
fprintf(stderr, "Error: read_int");
exit(EXIT_FAILURE);
}
return ret;
}


void write_int(FILE *fp, int n)
{
if(fwrite(&n, sizeof(int), 1, fp) < 1)
{
fprintf(stderr, "Error : write_int");
exit(EXIT_FAILURE);
}
}


int main(void)
{
int i, n;
char const *filename =3D "test.txt";
FILE *fp =3D NULL;

/* write */
fp =3D xfopen(filename, "w");
for(i =3D 0; i < 3; i++)
write_int(fp, i);
fclose(fp); /* 1 2 3 */

/* read / write */
fp =3D xfopen(filename, "rb+");
n =3D read_int(fp);
printf("n =3D %d\n", n);
write_int(fp, n + 10);
fclose(fp); /* 1 11 3 */

/* read */
fp =3D xfopen(filename, "r");
for(i =3D 0; i < 3; i++)
printf("%d\n", read_int(fp));
fclose(fp);

return 0;
}

Je commence par =E9crire 3 entiers dans un fichier.
J'ouvre une deuxi=E8me fois le fichier en mode rb+, je lis le premier
entier, et j'=E9cris =E0 la suite.

Probl=E8me, sous Ubuntu, cette =E9criture fonctionne bien, et l'affichage
du contenu du fichier me donne bien 1, 11, 3, qui est le r=E9sultat
attendu.
Sous Windows par contre, le fichier contient toujours 1, 2, 3.

J'ai fait quelques tests avec ftell entre la lecture et l'=E9criture, et
la valeur retourn=E9e est normale(la taille d'un entier).

Une explication?

Merci d'avance.

10 réponses

1 2
Avatar
GurneyH
On 29 nov, 10:14, GurneyH wrote:
Bonjour.

Je suis peu habitué à manipuler des fichiers en lecture/écriture, e t
je me trouve avec un code qui produit un résultat différent suivant
qu'il est compilé et exécuté sous Ubuntu et GCC ou Windows et MingW .

#include <stdio.h>
#include <stdlib.h>

FILE *xfopen(char const *path, const char *mode)
{
    FILE *fp = fopen(path, mode);
    if(fp == NULL)
    {
        perror("xfopen ");
        exit(EXIT_FAILURE);
    }
    return fp;

}

int read_int(FILE *fp)
{
    int ret;
    if(fread(&ret, sizeof(int), 1, fp) < 1)
    {
        fprintf(stderr, "Error: read_int");
        exit(EXIT_FAILURE);
    }
    return ret;

}

void write_int(FILE *fp, int n)
{
    if(fwrite(&n, sizeof(int), 1, fp) < 1)
    {
        fprintf(stderr, "Error :  write_int");
        exit(EXIT_FAILURE);
    }

}

int main(void)
{
    int i, n;
    char const *filename = "test.txt";
    FILE *fp = NULL;

    /* write */
    fp = xfopen(filename, "w");
    for(i = 0; i < 3; i++)
        write_int(fp, i);
    fclose(fp); /* 1 2 3 */

    /* read / write */
    fp = xfopen(filename, "rb+");
    n = read_int(fp);
    printf("n = %dn", n);
    write_int(fp, n + 10);
    fclose(fp); /* 1 11 3 */

    /* read */
    fp = xfopen(filename, "r");
    for(i = 0; i < 3; i++)
        printf("%dn", read_int(fp));
    fclose(fp);

    return 0;

}

Je commence par écrire 3 entiers dans un fichier.
J'ouvre une deuxième fois le fichier en mode rb+, je lis le premier
entier, et j'écris à la suite.

Problème, sous Ubuntu, cette écriture fonctionne bien, et l'affichage
du contenu du fichier me donne bien 1, 11, 3, qui est le résultat
attendu.
Sous Windows par contre, le fichier contient toujours 1, 2, 3.

J'ai fait quelques tests avec ftell entre la lecture et l'écriture, et
la valeur retournée est normale(la taille d'un entier).

Une explication?

Merci d'avance.



Navré, il y a plusieurs coquilles dans le code.
Le même corrigé.
#include <stdio.h>
#include <stdlib.h>

FILE *xfopen(char const *path, const char *mode)
{
FILE *fp = fopen(path, mode);
if(fp == NULL)
{
perror("xfopen ");
exit(EXIT_FAILURE);
}
return fp;

}

int read_int(FILE *fp)
{
int ret;
if(fread(&ret, sizeof(int), 1, fp) < 1)
{
fprintf(stderr, "Error: read_int");
exit(EXIT_FAILURE);
}
return ret;

}

void write_int(FILE *fp, int n)
{
if(fwrite(&n, sizeof(int), 1, fp) < 1)
{
fprintf(stderr, "Error : write_int");
exit(EXIT_FAILURE);
}

}

int main(void)
{
int i, n;
char const *filename = "test.txt";
FILE *fp = NULL;

/* write */
fp = xfopen(filename, "wb");
for(i = 0; i < 3; i++)
write_int(fp, i + 1);
fclose(fp); /* 1 2 3 */

/* read / write */
fp = xfopen(filename, "rb+");
n = read_int(fp);
printf("n = %dn", n);
write_int(fp, n + 10);
fclose(fp); /* 1 11 3 */

/* read */
fp = xfopen(filename, "rb");
for(i = 0; i < 3; i++)
printf("%dn", read_int(fp));
fclose(fp);

return 0;

}
Avatar
Lucas Levrel
Le 29 novembre 2010, GurneyH a écrit :

Sous Windows par contre, le fichier contient toujours 1, 2, 3.



Les droits d'accès sur test.txt sont-ils bons ?

--
LL
Avatar
GurneyH
On 29 nov, 11:26, Lucas Levrel wrote:
Le 29 novembre 2010, GurneyH a écrit :

> Sous Windows par contre, le fichier contient toujours 1, 2, 3.

Les droits d'accès sur test.txt sont-ils bons ?

--
LL



Je ne pense pas.
Le fichier est créé par le programme. (premier fopen avec "w").
De plus, si c'était un problème de droits d'accès, fopen retournerait
un pointeur NULL, et perror m'indiquerait ce problème, non?
Avatar
Antoine Leca
GurneyH écrivit :
Je suis peu habitué à manipuler des fichiers en lecture/écriture, et
je me trouve avec un code qui produit un résultat différent suivant
qu'il est compilé et exécuté sous Ubuntu et GCC ou Windows et MingW.





Ta première version écrivait en texte et relisait en binaire, clairement
c'est un mélange des genres... mais c'est corrigé.

Cela étant, les fichiers binaires ou textes n'ont rien à voir dans ton
problème : tu obtiens exactement le même résultat avec des fichiers texte.


Je commence par écrire 3 entiers dans un fichier.

J'ouvre une deuxième fois le fichier en mode rb+, je lis le premier
entier, et j'écris à la suite.

Problème, sous Ubuntu, cette écriture fonctionne bien, et l'affichage
du contenu du fichier me donne bien 1, 11, 3, qui est le résultat
attendu.
Sous Windows par contre, le fichier contient toujours 1, 2, 3.





Oui, j'obtiens la même chose, y compris avec SUA (le POSIX de Windows).
Ce qui me semble une non-conformité de la part de Microsoft.


Une explication?





À vue de nez, l'implémentation de stdio de Microsoft ne respecte pas la
norme pour un fichier ouvert en "r+" ou "rb+" : si tu fais une lecture
suivie directement d'une écriture (sans remise-à-zéro de l'état du
tampon), l'écriture n'est pas prise en compte au moment de fermer le
fichier, l'implémentation fait comme si le fichier était encore en mode
lecture et ne met pas à jour le disque :-(

C'est ÀMHA non conforme : la norme précise bien que si tu fais le
contraire (écriture suivi de lecture), un programme pour être conforme
doit faire une remise-à-zéro intermédiaire : en creux, cela veut dire
que dans le sens que tu utilises, cela doit fonctionner.


En même temps, la solution est « simple » : il suffit de rajouter une
opération de remise-à-zéro même si c'est officiellement inutile: essaye
n = read_int(fp);
printf("n = %dn", n);
fseek(fp, 0L, SEEK_CUR); /* remise à zéro du tampon */
write_int(fp, n + 10);


Antoine
Avatar
GurneyH
On 29 nov, 15:00, Antoine Leca wrote:
GurneyH écrivit :

>> Je suis peu habitué à manipuler des fichiers en lecture/écriture , et
>> je me trouve avec un code qui produit un résultat différent suivan t
>> qu'il est compilé et exécuté sous Ubuntu et GCC ou Windows et Mi ngW.

Ta première version écrivait en texte et relisait en binaire, clairem ent
c'est un mélange des genres... mais c'est corrigé.

Cela étant, les fichiers binaires ou textes n'ont rien à voir dans to n
problème : tu obtiens exactement le même résultat avec des fichiers texte.

>> Je commence par écrire 3 entiers dans un fichier.

>> J'ouvre une deuxième fois le fichier en mode rb+, je lis le premier
>> entier, et j'écris à la suite.

>> Problème, sous Ubuntu, cette écriture fonctionne bien, et l'affich age
>> du contenu du fichier me donne bien 1, 11, 3, qui est le résultat
>> attendu.
>> Sous Windows par contre, le fichier contient toujours 1, 2, 3.

Oui, j'obtiens la même chose, y compris avec SUA (le POSIX de Windows).
Ce qui me semble une non-conformité de la part de Microsoft.

>> Une explication?

À vue de nez, l'implémentation de stdio de Microsoft ne respecte pas la
norme pour un fichier ouvert en "r+" ou "rb+" : si tu fais une lecture
suivie directement d'une écriture (sans remise-à-zéro de l'état d u
tampon), l'écriture n'est pas prise en compte au moment de fermer le
fichier, l'implémentation fait comme si le fichier était encore en mo de
lecture et ne met pas à jour le disque :-(

C'est ÀMHA non conforme : la norme précise bien que si tu fais le
contraire (écriture suivi de lecture), un programme pour être conform e
doit faire une remise-à-zéro intermédiaire : en creux, cela veut di re
que dans le sens que tu utilises, cela doit fonctionner.

En même temps, la solution est « simple » : il suffit de rajouter u ne
opération de remise-à-zéro même si c'est officiellement inutile: essaye
    n = read_int(fp);
    printf("n = %dn", n);
    fseek(fp, 0L, SEEK_CUR);    /* remise à zéro du tampon */
    write_int(fp, n + 10);

Antoine



En effet, le code fonctionne en plaçant
fseek(fp, 0L, SEEK_CUR); /* remise à zéro du tampon */
entre les 2 opérations. :)

Merci beaucoup pour la solution, ainsi que pour l'explication .

GurneyH
Avatar
-ed-
On 29 nov, 15:00, Antoine Leca wrote:

À vue de nez, l'implémentation de stdio de Microsoft ne respecte pas la
norme pour un fichier ouvert en "r+" ou "rb+" : si tu fais une lecture
suivie directement d'une écriture (sans remise-à-zéro de l'état d u
tampon), l'écriture n'est pas prise en compte au moment de fermer le
fichier, l'implémentation fait comme si le fichier était encore en mo de
lecture et ne met pas à jour le disque :-(

C'est ÀMHA non conforme : la norme précise bien que si tu fais le
contraire (écriture suivi de lecture), un programme pour être conform e
doit faire une remise-à-zéro intermédiaire : en creux, cela veut di re
que dans le sens que tu utilises, cela doit fonctionner.

En même temps, la solution est « simple » : il suffit de rajouter u ne
opération de remise-à-zéro même si c'est officiellement inutile: essaye
    n = read_int(fp);
    printf("n = %dn", n);
    fseek(fp, 0L, SEEK_CUR);    /* remise à zéro du tampon */
    write_int(fp, n + 10);

Antoine



Ah, ben j'ai appris un truc. C'est bien.

Merci Antoine !
Avatar
GurneyH
On 29 nov, 15:00, Antoine Leca wrote:
GurneyH écrivit :

>> Je suis peu habitué à manipuler des fichiers en lecture/écriture , et
>> je me trouve avec un code qui produit un résultat différent suivan t
>> qu'il est compilé et exécuté sous Ubuntu et GCC ou Windows et Mi ngW.

Ta première version écrivait en texte et relisait en binaire, clairem ent
c'est un mélange des genres... mais c'est corrigé.

Cela étant, les fichiers binaires ou textes n'ont rien à voir dans to n
problème : tu obtiens exactement le même résultat avec des fichiers texte.

>> Je commence par écrire 3 entiers dans un fichier.

>> J'ouvre une deuxième fois le fichier en mode rb+, je lis le premier
>> entier, et j'écris à la suite.

>> Problème, sous Ubuntu, cette écriture fonctionne bien, et l'affich age
>> du contenu du fichier me donne bien 1, 11, 3, qui est le résultat
>> attendu.
>> Sous Windows par contre, le fichier contient toujours 1, 2, 3.

Oui, j'obtiens la même chose, y compris avec SUA (le POSIX de Windows).
Ce qui me semble une non-conformité de la part de Microsoft.

>> Une explication?

À vue de nez, l'implémentation de stdio de Microsoft ne respecte pas la
norme pour un fichier ouvert en "r+" ou "rb+" : si tu fais une lecture
suivie directement d'une écriture (sans remise-à-zéro de l'état d u
tampon), l'écriture n'est pas prise en compte au moment de fermer le
fichier, l'implémentation fait comme si le fichier était encore en mo de
lecture et ne met pas à jour le disque :-(

C'est ÀMHA non conforme : la norme précise bien que si tu fais le
contraire (écriture suivi de lecture), un programme pour être conform e
doit faire une remise-à-zéro intermédiaire : en creux, cela veut di re
que dans le sens que tu utilises, cela doit fonctionner.

En même temps, la solution est « simple » : il suffit de rajouter u ne
opération de remise-à-zéro même si c'est officiellement inutile: essaye
    n = read_int(fp);
    printf("n = %dn", n);
    fseek(fp, 0L, SEEK_CUR);    /* remise à zéro du tampon */
    write_int(fp, n + 10);

Antoine



Bonjour,

En consultant la norme C99, on peut lire

Norme C99 7.19.5.3

"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."

En particulier ce passage
"input shall not be directly followed by output without an intervening
call to a file positioning function". qui revient à la solution que
donnée Antoine Leca plus haut.

De la norme, je comprend:
Avec un fichier ouvert en mode "+"
lecture -> (rewind, fpos, fseek) -> écriture
éciture -> fflush -> lecture

Finalement, Il semble que le comportement sous Windows soit conforme.

Je termine par une question.
A votre connaissance, l'utilisation du mode "+" est, ou reste
anecdotique?

Merci

GurneyH
Avatar
GurneyH
On 1 déc, 04:10, GurneyH wrote:
On 29 nov, 15:00, Antoine Leca wrote:



> GurneyH écrivit :

> >> Je suis peu habitué à manipuler des fichiers en lecture/écritu re, et
> >> je me trouve avec un code qui produit un résultat différent suiv ant
> >> qu'il est compilé et exécuté sous Ubuntu et GCC ou Windows et MingW.

> Ta première version écrivait en texte et relisait en binaire, clair ement
> c'est un mélange des genres... mais c'est corrigé.

> Cela étant, les fichiers binaires ou textes n'ont rien à voir dans ton
> problème : tu obtiens exactement le même résultat avec des fichie rs texte.

> >> Je commence par écrire 3 entiers dans un fichier.

> >> J'ouvre une deuxième fois le fichier en mode rb+, je lis le premie r
> >> entier, et j'écris à la suite.

> >> Problème, sous Ubuntu, cette écriture fonctionne bien, et l'affi chage
> >> du contenu du fichier me donne bien 1, 11, 3, qui est le résultat
> >> attendu.
> >> Sous Windows par contre, le fichier contient toujours 1, 2, 3.

> Oui, j'obtiens la même chose, y compris avec SUA (le POSIX de Windows ).
> Ce qui me semble une non-conformité de la part de Microsoft.

> >> Une explication?

> À vue de nez, l'implémentation de stdio de Microsoft ne respecte pa s la
> norme pour un fichier ouvert en "r+" ou "rb+" : si tu fais une lecture
> suivie directement d'une écriture (sans remise-à-zéro de l'état du
> tampon), l'écriture n'est pas prise en compte au moment de fermer le
> fichier, l'implémentation fait comme si le fichier était encore en mode
> lecture et ne met pas à jour le disque :-(

> C'est ÀMHA non conforme : la norme précise bien que si tu fais le
> contraire (écriture suivi de lecture), un programme pour être confo rme
> doit faire une remise-à-zéro intermédiaire : en creux, cela veut dire
> que dans le sens que tu utilises, cela doit fonctionner.

> En même temps, la solution est « simple » : il suffit de rajouter une
> opération de remise-à-zéro même si c'est officiellement inutile : essaye
>     n = read_int(fp);
>     printf("n = %dn", n);
>     fseek(fp, 0L, SEEK_CUR);    /* remise à zéro du tampon */
>     write_int(fp, n + 10);

> Antoine

Bonjour,

En consultant la norme C99, on peut lire

Norme C99 7.19.5.3

"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."

En particulier ce passage
"input shall not be directly followed by output without an intervening
call to a file positioning function". qui revient à la solution que
donnée Antoine Leca plus haut.

De la norme, je comprend:
Avec un fichier ouvert en mode "+"
lecture -> (rewind, fpos, fseek) -> écriture
éciture -> fflush -> lecture

Finalement, Il semble que le comportement sous Windows soit conforme.

Je termine par une question.
A votre connaissance, l'utilisation du mode "+" est, ou reste
anecdotique?

Merci

GurneyH

A votre connaissance, l'utilisation du mode "+" est, ou reste
anecdotique?


Je mange mes mots...

A votre connaissance, l'utilisation du mode "+" est fréquente, ou
reste anecdotique?

Merci
Avatar
-ed-
On 1 déc, 04:28, GurneyH wrote:

A votre connaissance, l'utilisation du mode "+" est fréquente, ou
reste anecdotique?



Personnellement, j'évite, car c'est une magnifique machine à broyer
les fichiers ...
Avatar
Antoine Leca
GurneyH écrivit :
Norme C99 7.19.5.3

"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),



Jusque là j'avais lu...

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. [...]"



Mais j'avais sauté cette important phrase ! :+| Désolé.

Effectivement, il semble donc bien que la solution de Microsoft est
conforme. Pan sur le bec.


De la norme, je comprend:
Avec un fichier ouvert en mode "+"
lecture -> (rewind, fpos, fseek) -> écriture
éciture -> fflush -> lecture



lecture -> reçu EOF _ou_ rewind/fseek/fsetpos -> écriture

écriture -> fflush/rewind/fseek/fsetpos -> lecture


A votre connaissance, l'utilisation du mode "+" est fréquente,
ou reste anecdotique?



Je ne dirait pas que c'est fréquent, mais "r+" n'est pas anecdotique,
c'est le mode normal pour ouvrir un fichier que tu sais exister et que
tu veux modifier (et oui, ce n'est pas évident; le mode équivalent en
POSIX est O_RDWR, c'est quand même plus clair.)

"w+" et "a+" me paraissent eux anecdotiques, à mon sens ils sont plus là
pour la symétrie (de l'implémentation originale de stdio en 1976) que
pour autre chose.


Antoine
1 2