I was toying around with some code which was opening, reading, and modifying a text file. A quick (simplified) example would be:
#include <stdio.h>
int main()
{
FILE * fp = fopen("test.txt", "r+");
char line[100] = {'\0'};
int count = 0;
int ret_code = 0;
while(!feof(fp)){
fgets(line, 100, fp);
// do some processing on line...
count++;
if(count == 4) {
ret_code = fprintf(fp, "replaced this line\n");
printf("ret code was %d\n", ret_code);
perror("Error was: ");
}
}
fclose(fp);
return 0;
}
Now on Linux, compiled with gcc (4.6.2) this code runs, and modifies the file's 5th line. The same code, running on Windows7 compiled with Visual C++2010 runs and claims to have succeeded (reports a return code of 19 characters and perror says "No error") but fails to replace the line.
On Linux my file has full permissions:
-rw-rw-rw- 1 mike users 191 Feb 14 10:11 test.txt
And as far as I can tell it's the same on Windows:
test.txt (right click) -> properties -> Security
"Allow" is checked for Read & Write for user, System, and Admin.
I get the same results using MinGW's gcc on Windows so I know it's not a Visual C++ "feature".
Am I missing something obvious, or is the fact that I get no errors, but also no output just an undocumented "feature" of using r+ with fopen() on Windows?
EDIT: Seems even at Microsoft's site they say "r+" should open for reading and writting. They also made this note:
When the "r+", "w+", or "a+" access type is specified, both reading and writing are allowed (the file is said to be open for "update"). 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.
So I tried:
...
if(count == 4) {
fflush(fp);
ret_code = fprintf(fp, "replaced this line\n");
fflush(fp);
printf("ret code was %d\n", ret_code);
...
to no avail.
According to the Linux man page for fopen():
Reads and writes may be intermixed on read/write streams in any order.
Note that ANSI C requires that a file positioning function intervene
between output and input, unless an input operation encounters
end-of-file. (If this condition is not met, then a read is allowed to
return the result of writes other than the most recent.) Therefore it
is good practice (and indeed sometimes necessary under Linux) to put
an fseek(3) or fgetpos(3) operation between write and read operations
on such a stream. This operation may be an apparent no-op (as in
fseek(..., 0L, SEEK_CUR) called for its synchronizing side effect.
So, you should always call fseek() (as, eg. fseek(..., 0, SEEK_CUR)) when switching between reading and writing from a file.
Before performing output after input, an fflush() isn't any good - you need to perform a seek operation. Something like:
fseek(fp, ftell(fp), SEEK_SET); // not fflush(fp);
from the C99 standard (7.19.5.3/6 "The fopen functoin):
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.
Related
I have following problem:
void edit(FILE *f, int cnt)
{
int i = 0;
int offset = 0;
rewind(f);
schedule todo;
schedule *p = &todo;
fprintf(stdout, "\n%s\n", "------------------------------------------------");
fread(&todo, sizeof(schedule), 1, f);
while (!feof(f)) {
fprintf(stdout, "%6d%18s\n",
++i, todo.title);
fread(&todo, sizeof(schedule), 1, f);
}
fprintf(stdout, "%s\n\n", "-------------------------------------------------");
fprintf(stdout, "%s\n\n", "Number: ");
scanf("%d", &i);
getchar();
rewind(f);
offset = (long) sizeof(schedule);
fseek(f, (i - 1)*offset, SEEK_CUR);
fread(&todo, sizeof(schedule), 1, f);
printf("Edit: %s\n", todo.title);
fprintf(stdout, "%6d%18s%8s%10s%8d\n",
todo.number, todo.title, todo.where, todo.details, todo.importance);
scanf("%s", todo.title);
fwrite(&todo, (long)sizeof(todo.title), 1, f);
}
It's part of editing data codes.
This is what I expected.
If a user put a number(i in the code), the program will find the location (in binary file).
Then, the user put todo.title by (scanf("%s", todo.title);) and the program will edit it by using
fwrite(&todo, (long)sizeof(todo.title), 1, f);
I got a warning like
Expression: ("Flush between consecutive read and write.", !stream.has_any_of(_IOREAD))
I think there's problems with buffer, but I can't fix this.
If you have a file open for update (read and write) then the C11 standard requires:
§7.21.5.3 The fopen function
¶7 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.
The quote comes from the C11 specification, but the wording is essentially unchanged in all versions of the standard.
Note that you must do a seek operation, even if it is just fseek(f, 0, SEEK_CUR), between a read and a write operation, and between a write and a read operation.
Your code has an fread() followed by an fwrite() with no intervening fseek(). That's probably an oversight since you change the record and overwrite the next record in the file with the updated information. You probably need an fseek(f, -(long)sizeof(schedule), SEEK_CUR) or thereabouts to move back and overwrite the record just read and changed.
Could someone tell me why the file doesn't change? It works when I use rewind or fseek but not otherwise.
What's the standard way of using fputs after fgets. The file indicator is at position 9 so fputs must write after that, but it doesn't do anything.
In file:
abcd efgh ijkl mnor
In source code:
char c;
char str[15];
FILE *fp = fopen("d:\\data.txt","r+");
fgets(str, 10, fp);
// fseek(fp, 9, SEEK_SET);
// rewind(fp);
printf("%d\n", ftell(fp));
// ftel shows that it's in "9".
printf("%s", str);
fputs(str, fp);
// why its not working
fclose(fp);
Regarding the definition of fopen/'+' in the C standard (e.g. as in this online C standard draft), switching from reading to writing requires an intermediate call to a file positioning function (emphasis are mine):
7.21.5.3 The fopen function
(7) 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.
So I'd suggest you write the following code to overcome your problem:
fseek ( fp , 0, SEEK_CUR);
fputs(str, fp);
The MS documentation for fopen says this:
When the "r+", "w+", or "a+" access type is specified, both
reading and writing are enabled (the file is said to be open for
"update"). However, when you switch from reading to writing, the input
operation must encounter an EOF marker. If there is no EOF, you
must use an intervening call to a file positioning function. The file
positioning functions are fsetpos, fseek, and rewind. When you
switch from writing to reading, you must use an intervening call to
either fflush or to a file positioning function.
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).
I have been trying out some file io and have written the following:
#include <stdio.h>
int main(){
char stuff [80];
FILE *file;
file=fopen("hello.cheese", "w+");
fprintf(file, "%s", "cheese");
fscanf(file, "%s", stuff);
printf("%s", stuff);
fprintf(file, "\n%s", stuff);
fclose(file);
return 0;
}
All this does is put ^A in the file (next line underneath cheese) and print nothing.
You might want to read closely fopen description, especially
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.
You used 'w+' mode, which allows reading and writing, but did not flush, nor rewind file pointer after write.
As a side note, Your code could use some error checking (NULL != file).
After opening a file in append update mode, is it necessary to execute a file positioning statement before each write to the file?
FILE *h;
int ch;
if ((h = fopen("data", "a+")) == NULL) exit(1);
if (fseek(h, 0 SEEK_SET)) exit(2);
ch = fgetc(h); /* read very first character */
if (ch == EOF) exit(3);
/* redundant? mandatory? */
fseek(h, 0, SEEK_END); /* call file positioning before output */
/* add 1st character to the end of file on a single line*/
fprintf(h, "%c\n", ch);
The C11 Standard says:
7.21.5.3/6 ... all subsequent writes to the file to be forced to the then current end-of-file ...
and
7.21.5.3/7 ... input shall not be directly followed by output without an
intervening call to a file positioning function ...
I take it the shall in 7.21.5.3/7 is stronger than the description in 7.21.5.3/6.
Probably not redundant in portable C. While the underlying file descriptor will always append (at least on Unix), the point of the fseek/fflush requirement is to get rid of the input buffer before writing to the output, so that the same buffer can be used for reading and writing. AFAIK you're not even required to seek to end of file, you can seek anywhere, as long as you seek.
The second description is stronger than the first, but that is to be expected. The first only states that all writes go to EOF, i.e. that there's no way to write anywhere else. The second establishes the rule that switching from reading to writing must be accompanied by a flush or seek, to ensure that read and write aspects of the buffer don't get mixed up.