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.
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.
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).
A program that runs just fine on my freeBSD system fails when I build it on windows (Visual Studio 15). It goes into an endless loop here:
//...
while (1) {
if ('#' == fgetc(f)) {
// we do some stuff here. irrelevant for stackoverflow question
break;
}
fseek(f, -1, SEEK_CUR);
if (0 != fseek(f, -1, SEEK_CUR)) {
// Beginning of file.
break;
}
}
//...
On closer look (by adding a bunch of fgetpos()-calls) I find that fgetc moves the file position indicator backwards. So it misses the beginning of the file and some '#' if they are not in a multiple-of-3 position from the end.
I notice that this only happenes when the file f is opened with
fopen(filename, "a+");
//text mode read/append
When I change it to
fopen(filename, "ab+");
//binary mode read/append
then everything works as expected.
I think for my code it is safe just to use binary mode all the time.
But two questions remain:
Are there reasons that stand against binary mode?
What trickery is this with wrong direction in text mode?
Quoting C11 7.21.9.2 the fseek function:
For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET.
Invoking fseek with a whence argument of SEEK_CUR on a stream open in text mode is not covered by the C Standard. Opening the file in binary mode seems a much better option.
The value returned by fgetpos() may not be meaningful as an offset in the file, it is only meant to be passed as an argument to fsetpos().
As a general remark, you should try and change you algorithms to avoid relying on backwards seeks in the stream, especially relying on fseek() errors seems unreliable. Instead save the position before the fgetc() with ftell() or fgetpos() and restore it when needed with fseek(pos, SEEK_SET, fp) or fsetpos().
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.
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.