Different behaviour of fwrite between Linux and Windows - c

I have a small example program written in C. I have a main that call a function writeFile that writes some numbers in a binary file. Then I call overwrite to replace 0 with 1 and finally I print the result.
This is the code:
#include <stdio.h>
/* Print the content of the file */
void printFile(){
printf("Read test.dat:\n");
int r;
FILE* fp = fopen("test.dat", "rb+");
if(fp) {
while(fread(&r,sizeof(int),1,fp)){
printf("%d\n", r);
}
}
fclose(fp);
}
/* Replace 0 with 1 */
void overwrite(){
int r;
FILE *fp = fopen("test.dat", "rb+");
if (fp) {
int i=0;
while (i < 4 && fread(&r, sizeof(int), 1, fp)) {
i++;
if (r == 0) {
r = 1;
fseek(fp,-sizeof(int),SEEK_CUR);
fwrite(&r,sizeof(int),1,fp);
}
}
}
fclose(fp);
}
/* Create original file */
void writeFile() {
int b, b1, b2, b3, b4;
b = 3;
b1 = 2;
b2 = 0;
b3 = 4;
FILE *fp = fopen("test.dat", "wb");
if (fp) {
fwrite(&b, sizeof(int), 1, fp);
fwrite(&b1, sizeof(int), 1, fp);
fwrite(&b2, sizeof(int), 1, fp);
fwrite(&b3, sizeof(int), 1, fp);
}
fclose(fp);
}
int main() {
writeFile();
printf("---------BEFORE--------\n");
printFile();
printf("-----------------------\n");
printf("Overwriting...\n");
overwrite();
printf("---------AFTER---------\n");
printFile();
return 0;
}
This code works with Linux, but when I run the same code on Windows the output is this:
---------BEFORE--------
Read test.dat:
3
2
0
4
-----------------------
Overwriting...
---------AFTER---------
Read test.dat:
3
2
1
2
Not only 0 was replaced by 1 but also the last number changed. Someone can help me to understand why this happens?
Another problem is that in the overwrite I must use i to stop the while because without the i<4 I get an infinite loop (only with Windows).
I tested this code on Windows 8.1 compiled with gcc 4.8.1 (from MinGW).
On my Linux machine I tested the code with gcc 5.1.1.
Thank you all,

It's because you need to fflush() after the fwrite(), since you should not call fread() after calling fwrite() without an intervening call to fflush(),
This is the section of the standard that is relevant in this case
7.21.5.3 The fopen function
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.
It's weird that this section is within fopen() function since it involves fread() and fwrite(), which is where I was looking for an answer.
You can also see that my previous answer worked but not for the reason I stated in it, instead the explanation is found in the paragraph above.

MSDN states for Stream I/O:
Input can follow output directly only with an intervening call to fflush or to a file-positioning function (fseek, fsetpos, or rewind). Output can follow input without an intervening call to a file-positioning function if the input operation encounters the end of the file.
So you'd have to call fflush() after calling fwrite:
void overwrite()
{
FILE *fp = fopen("test.dat", "rb+");
if (fp) {
int r;
while (fread(&r, sizeof(int), 1, fp) == 1) {
if (r == 0) {
r = 1;
fseek(fp,-sizeof(int),SEEK_CUR);
fwrite(&r,sizeof(int),1,fp);
fflush(fp); /* Flush here */
}
}
fclose(fp); /* Also: call fclose() only when fopen() succeeded */
}
}

Related

C Updating text file using r+ mode [duplicate]

I have a program which uses stdio for reading and writing a binary file. It caches the current stream position and will not seek if the read/write offset is already at the desired position.
However, an interesting problem appears, that when a byte is read and the following byte is written, it doesn't actually get written!
Here is a program to reproduce the problem:
#include <cstdio>
int main() {
FILE *f = fopen("test.bin", "wb");
unsigned char d[1024] = { 0 };
fwrite(d, 1, 1024, f);
fclose(f);
f = fopen("test.bin", "rb+");
for (size_t i = 0; i < 1024; i++) {
unsigned char a[1] = { 255 - (unsigned char)(i) };
fflush(f);
fwrite(a, 1, 1, f);
fflush(f);
fseek(f, i, SEEK_SET);
fread(a, 1, 1, f);
printf("%02X ", a[0]);
}
fclose(f);
return 0;
}
You are supposed to see it write the bytes FF down to 00, however only the first byte is written because it does not follow a fread immediately.
If it seeks before fwrite, it acts correctly.
The problem happens on Visual Studio 2010/2012 and TDM-GCC 4.7.1 (Windows), however it works on codepad which I guess is due to it being executed on Linux.
Any idea why this happens?
C99 §7.18.5.3/6 (quoted from N869 final draft):
“When a file is opened with update mode (’+’ as the second or third character in the
above list of mode argument values) […] 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.”

fflush required on windows but not Linux

I am unable to find a documented reason why I need the fflush at the end of the for loop in the following code on windows but not on linux. This is to xor one file with another. without the fflush on windows it seems to read only to the second position in the file I am updating and never reads any further filling the file with that character xor'd with the second file. The fflush makes it work as I expect but runs slowly. On linux the fflush makes no difference, it works either way. I am using gcc on both systems but the windows compiler does the same thing.
#include < stdio.h>
#include < string.h>
#include < stdlib.h>
#include < limits.h>
int main(int argc, char *argv[])
{
FILE *pf , *kf;
int index, fsize, ksize, fpos;
unsigned char t_char, k_char, c_char;
pf = fopen(argv[1],"rb+"); /* open the file for I/O */
kf = fopen(argv[2],"rb+"); /* open key file */
fseek(pf, 0, SEEK_END);
fsize = ftell(pf);
printf(" Input size = %d\n", fsize);
rewind(pf);
fseek(kf, 0, SEEK_END);
ksize = ftell(kf);
printf(" keyfile size = %d\n", ksize);
rewind(kf);
if (ksize < fsize) {
printf(" Keyfile must be equal to or longer than crypt file\n");
exit(0);
}
for(index=0; index<fsize; index++) {
t_char = fgetc(pf);
k_char = fgetc(kf);
c_char = t_char ^ k_char;
fseek(pf, -1, SEEK_CUR);
fputc(c_char, pf);
/*fflush(pf);*/
}
fclose(pf);
fclose(kf);
return(0);
}
Quoted from: https://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html
When a file is opened with update mode ( '+' as the second or third
character in the mode argument), both input and output may be
performed on the associated stream. However, the application shall
ensure that output is not directly followed by input without an
intervening call to fflush() or to a file positioning function (
fseek(), fsetpos(), or rewind()), and input is not directly followed
by output without an intervening call to a file positioning function,
unless the input operation encounters end-of-file.
So this is necessary no matter which OS you are using, according to the documentation.
However, you could use a file positioning function, instead of fflush, and see if that is faster.
Alternatively you could copy the file content to memory and do the xor operations there, and write everything back to the file once its finished.

Does rewind and fsetpos can also intervene the C file append mode subsequent writes?

C11 Working Draft Standard N1570 said that on page 306 at "7.21.5.3 The fopen function" section
Opening a file with append mode ('a' as the first character in the mode argument) causes all subsequent writes to the file to be forced to the then current end-of-file, regardless of intervening calls to the fseek function. ...
Can anybody confirms that not only fseek function but also other file positioning function such as fsetpos and rewind can't intervene the append mode?
Well, the standard on page 338 said that rewind does a similar thing with (void)fseek(stream, 0L, SEEK_SET) except that the error indicator for the stream is also cleared. But as far as I have read on page 337 the standard does not state that fsetpos similar to fseek.
Source Code to illustrate this problem
#include <stdio.h>
// Declare the File Pointer
FILE *fp;
// Declare the test integer variable
int TEST;
// declare the test position variable for fsetpos test
fpos_t POSITION;
void WriteData(int choice){
// Clear the file content
fp = fopen("test.bin", "wb"); fclose(fp);
// Reopen the file with ab
fp = fopen("test.bin", "ab");
// Initialize the test integer variable
TEST = 100;
// Write five sample data
int i;
for(i = 1; i <= 5; i++) {
fwrite(&TEST, sizeof(TEST), 1, fp);
// If two data were written then save the position
if( i == 2 )
fgetpos(fp, &POSITION);
}
// Change the data
TEST = 54321;
// Declare the test case
switch(choice){
case 1 : fseek(fp, (long) 2*sizeof(TEST), SEEK_SET); break;
case 2 : rewind(fp); break;
case 3 : fsetpos(fp, &POSITION); break;
}
// Write the data again
fwrite(&TEST, sizeof(TEST), 1, fp);
// Close the file
fclose(fp);
}
void ReadData(){
// Open the file for read
fp = fopen("test.bin", "rb");
printf("\n [OUTPUT]");
// while the data can be read then print it to the console
while( fread(&TEST, sizeof(TEST), 1, fp) == 1)
printf("\n %d", TEST);
// Close the file
fclose(fp);
}
int main(){
/* Test Case Process */
printf("\n\n Intervene using fseek(fp, (long) 2*sizeof(TEST), SEEK_SET);");
WriteData(1);
ReadData();
printf("\n\n Intervene using rewind(fp);");
WriteData(2);
ReadData();
printf("\n\n Intervene using fsetpos(fp, &POSITION);");
WriteData(3);
ReadData();
return 0;
}
The desired output:
Intervene using fseek(fp, (long) 2*sizeof(TEST), SEEK_SET);
[OUTPUT]
100
100
100
100
100
54321
Intervene using rewind(fp);
[OUTPUT]
100
100
100
100
100
54321
Intervene using fsetpos(fp, &POSITION);
[OUTPUT]
100
100
100
100
100
54321
If the output were like that, then the standard does confirms that not only fseek function that can't intervene the subsequent writes, but also rewind and fsetpos. Else if the output were not like that, then the standard does not confirms it.
I have test it on windows 10 using tdm-gcc 4.9.2 and on Ubuntu 16.04 gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4). The result is that three of them can't intervene, but I'm not sure in other platform whether it can't intervene or not.
Finally, If I might re-ask my question again. Does the standard also confirms that not only fseek function that can't intervene the seubsequent writes, but also rewind and fsetpos?
If the standard confirms then please explain where does the statement which indicate the confirmation.
Else if the standard does not confirms then please explain where does the statement which indicate the denial.
About my question, what I need to know is the certainty that standard does confirms the compiler writer must exactly do this or do that etc. in order to confirms the standard, nothing more
With stream I/O, neither rewind() nor fsetpos() overrides the append mode — any more than fseek() does. The implicit seek for append mode happens when the write occurs; the positioning operation is separate and only affects where the implicit seek starts from, not where the write occurs.
With file descriptor I/O, pwrite() — positioned write — does override append mode but other operations do not. POSIX states: The pwrite() function shall be equivalent to write(), except that it writes into a given position and does not change the file offset (regardless of whether O_APPEND is set).

fscanf while-loop never runs

I'm working on a project, and I can't seem to figure out why a piece of my function for finding prime numbers won't run. Essentially, I want to code to first check the text file log for any previously encountered prime numbers, but no matter what I put for the while-loop containing fscanf(), it seems like my code never enters it.
int filePrime(int a) {
int hold = 0;
FILE *fp = fopen("primes.txt", "a+");
if (fp == NULL) {
printf("Error while opening file.");
exit(2);
}
/*
the while loop below this block is the one with the issue.
on first run, it should skip this loop entirely, and proceed
to finding prime numbers the old-fashioned way, while populating the file.
instead, it is skipping this loop and proceeding right into generating a
new set of prime numbers and writing them to the file, even if the previous
numbers are already in the file
*/
while (fscanf(fp, "%d", &hold) == 1){
printf("Inside scan loop.");
if (hold >= a) {
fclose(fp);
return 1;
}
if (a % hold == 0) {
fclose(fp);
return 0;
}
}
printf("Between scan and print.\n");
for (; hold <= a; hold++) {
if (isPrime(hold) == 1) {
printf("Printing %d to file\n", hold);
fprintf(fp, "%d\n", hold);
if (hold == a)
return 1;
}
}
fclose(fp);
return 0;
}
I have tried all sorts of changes to the while-loop test.
Ex. != 0, != EOF, cutting off the == 1 entirely.
I just can't seem to get my code to enter the loop using fscanf.
Any help is very much appreciated, thank you so much for your time.
In a comment, I asked where the "a+" mode leaves the current position?
On Mac OS X 10.11.4, using "a+" mode opens the file and positions the read/write position at the end of file.
Demo code (aplus.c):
#include <stdio.h>
int main(void)
{
const char source[] = "aplus.c";
FILE *fp = fopen(source, "a+");
if (fp == NULL)
{
fprintf(stderr, "Failed to open file %s\n", source);
}
else
{
int n;
char buffer[128];
fseek(fp, 0L, SEEK_SET);
while ((n = fscanf(fp, "%127s", buffer)) == 1)
printf("[%s]\n", buffer);
printf("n = %d\n", n);
fclose(fp);
}
return(0);
}
Without the fseek(), the return value from n is -1 (EOF) immediately.
With the fseek(), the data (source code) can be read.
One thing slightly puzzles me: I can't find information in the POSIX fopen() specification (or in the C standard) which mentions the read/write position after opening a file with "a+" mode. It's clear that write operations will always be at the end, regardless of intervening uses of fseek().
POSIX stipulates that the call to open() shall use O_RDWR|O_CREAT|O_APPEND for "a+", and open() specifies:
The file offset used to mark the current position within the file shall be set to the beginning of the file.
However, as chux notes (thanks!), the C standard explicitly says:
Annex J Portability issues
J.3 Implementation-defined behaviour
J.3.12 Library functions
…
Whether the file position indicator of an append-mode stream is initially positioned at
the beginning or end of the file (7.21.3).
…
So the behaviour seen is permissible in the C standard.
The manual page on Mac OS X for fopen() says:
"a+" — Open for reading and writing. The file is created if it does not exist. The stream is positioned at the end of the file. Subsequent writes to the file will always end up at the then current end of file, irrespective of any intervening fseek(3) or similar.
This is allowed by Standard C; it isn't clear it is fully POSIX-compliant.

fwrite() does not override text in Windows (C)

I write this C code so that I could test whether fwrite could update some values in a text file. I tested on Linux and it works fine. In Windows (vista 32bits), however, it simply does not work. The file remains unchanged after I write a different byte using: cont = fwrite(&newfield, sizeof(char), 1, fp);
The registers are written on the file using a "#" separator, in the format:
Reg1FirstField#Reg1SecondField#Reg2FirstField#Reg2SecondField...
The final file should be: First#1#Second#9#Third#1#
I also tried putc and fprintf, all with no result. Can someone please help me with this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct test {
char field1[20];
char field2;
} TEST;
int main(void) {
FILE *fp;
TEST reg, regread;
char regwrite[22];
int i, cont, charwritten;
fp=fopen("testupdate.txt","w+");
strcpy(reg.field1,"First");
reg.field2 = '1';
sprintf(regwrite,"%s#%c#", reg.field1, reg.field2);
cont = (int)strlen(regwrite);
charwritten = fwrite(regwrite,cont,1,fp);
fflush(fp);
strcpy(reg.field1,"Second");
reg.field2 = '1';
sprintf(regwrite,"%s#%c#", reg.field1, reg.field2);
cont = (int)strlen(regwrite);
charwritten = fwrite(regwrite,cont,1,fp);
fflush(fp);
strcpy(reg.field1,"Third");
reg.field2 = '1';
sprintf(regwrite,"%s#%c#", reg.field1, reg.field2);
cont = (int)strlen(regwrite);
charwritten = fwrite(regwrite,cont,1,fp);
fflush(fp);
fclose(fp);
// open file to update
fp=fopen("testupdate.txt","r+");
printf("\nUpdate field 2 on the second register:\n");
char aux[22];
// search for second register and update field 2
for (i = 0; i < 3; i ++) {
fscanf(fp,"%22[^#]#", aux);
printf("%d-1: %s\n", i, aux);
if (strcmp(aux, "Second") == 0) {
char newfield = '9';
cont = fwrite(&newfield, sizeof(char), 1, fp);
printf("written: %d bytes, char: %c\n", cont, newfield);
// goes back one byte in order to read properly
// on the next fscanf
fseek(fp,-1,SEEK_CUR);
}
fscanf(fp,"%22[^#]#", aux);
printf("%d-2: %s\n",i, aux);
aux[0] = '\0';
}
fflush(fp);
fclose(fp);
// open file to see if the update was made
fp=fopen("testupdate.txt","r");
for (i = 0; i < 3; i ++) {
fscanf(fp,"%22[^#]#", aux);
printf("%d-1: %s\n", i, aux);
fscanf(fp,"%22[^#]#",aux);
printf("%d-2: %s\n",i, aux);
aux[0] = '\0';
}
fclose(fp);
getchar();
return 0;
}
You're missing a file positioning function between the read and write. The Standard says:
7.19.5.3/6
When a file is opened with update mode, both input and output may be performed on the associated stream. However, ... 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. ...
for (i = 0; i < 3; i ++) {
fscanf(fp,"%22[^#]#", aux); /* read */
printf("%d-1: %s\n", i, aux);
if (strcmp(aux, "Second") == 0) {
char newfield = '9';
/* added a file positioning function */
fseek(fp, 0, SEEK_CUR); /* don't move */
cont = fwrite(&newfield, sizeof(char), 1, fp); /* write */
I didn't know it but here they explain it:
why fseek or fflush is always required between reading and writing in the read/write "+" modes
Conclusion: You must either fflush or fseek before every write when you use "+".
fseek(fp, 0, SEEK_CUR);
// or
fflush(fp);
cont = fwrite(&newfield, sizeof(char), 1, fp);
Fix verified on Cygwin.
You're not checking any return values for errors. I'm guessing the file is read-only and is not even opening properly.
At least here on OSX, your value 9 is begin appended to the end of the file ... so you're not updating the actual register value for Second at it's position in the file. For some reason after the scan for the appropriate point to modify the values, your stream pointer is actually at the end of the file. For instance, running and compiling your code on OSX produced the following output in the actual text file:
First#1#Second#1#Third#1#9
The reason your initial read-back is working is because the data is being written, but it's at the end of the file. So when you write the value and then back-up the stream and re-read the value, that works, but it's not being written in the location you're assuming.
Update: I've added some calls to ftell to see what's happening to the stream pointer, and it seems that your calls to fscanf are working as you'd assume, but the call to fwrite is jumping to the end of the file. Here's the modified output:
Update field 2 on the second register:
**Stream position: 0
0-1: First
0-2: 1
**Stream position: 8
1-1: Second
**Stream position before write: 15
**Stream position after write: 26
written: 1 bytes, char: 9
1-2: 9
**Stream position after read-back: 26
Update-2: It seems by simply saving the position of the stream-pointer, and then setting the position of the stream-pointer, the call to 'fwrite` worked without skipping to the end of the file. So I added:
fpos_t position;
fgetpos(fp, &position);
fsetpos(fp, &position);
right before the call to fwrite. Again, this is on OSX, you may see something different on Windows.
With this:
fp=fopen("testupdate.txt","w+");
^------ Notice the + sign
You opened the file in "append" mode -- that is what the plus sign does in this parameter. As a result, all of your fwrite() calls will be relative to the end of the file.
Using "r+" for the fopen() mode doesn't make sense -- the + means nothing in this case.
This and other issues with fopen() are why I prefer to use the POSIX-defined open().
To fix your particular case, get rid of the + characters from the fopen() modes, and consider that you might need to specify binary format on Windows ("wb" and "rb" modes).

Resources