If I have a text file with the following content opened as binary
1234567890
a call like this:
fseek(fp, 5L, SEEK_SET);
give me 6 when I call (char)fgetc(fp) because I offset 5 byte from byte 0 (not start from 1 but from 2)
but If I do:
fseek(fp, -3L, SEEK_END);
give me 8 and not 7 when I call (char)fgetc(fp).
Why? It seems as with SEEK_END the offset doesn't start from the previous byte after the last.
SEEK_END searches from the one-past last byte of the file:
1234567890 <--- bytes from the file
0123456789A <--- SEEK_SET-relative position
A9876543210 <--- SEEK_END-relative position (absolute value)
^
This is the (0, SEEK_END) byte
With this in mind, the very last byte of the file is the one found at (-1, SEEK_END) and thus the (-3, SEEK_END) byte is the 8.
Note that this is consistent with how C usually handles this kind of thing. For example a pointer to the end of a memory block will usually point to one-past the last byte of that block.
This also has the nice feature that you can get the size of the file with a call to fseek(SEEK_END) plus ftell(). No need to add or substract 1!
The man page from fseek() is a bit ambiguous about this issue, but compare with the man lseek that contains the same issue:
If whence is SEEK_END, the file offset shall be set to the size of the file plus offset.
In your example the size of the file is 10, and the offset is -3, so the final position is 10-3 = 7. And in offset 7 there is an 8.
fseek allows appending texts to a current file. Therefore the filepointer is set after (!) the last character in the file, because that is the place where new characters are to be appended.
From the start:
01234 <---position
ABCDEFGHIJK <---file content
From the end:
43210 <---position
ABCDEFGHIJK <---file content
So when you are fetching from the start, the 0th character is the A
And the 3rd is D.
But when you are fetching from the end, the 0th characters is EndOfFile
And the -3rd is I.
I think is because of the last character of file is '\n' or '\0' or something like that.
Related
This question popped up in a quiz:
Replace ‘Z’ with the proper number to set the FILE pointer ‘fp’ to the last byte of the file:
fseek( fp, Z, SEEK_END );
My answer was -1 because to me, it seemed natural that being at a certain byte meant being at a position such that appending to that file would overwrite that byte keep storing other bytes if any. The teacher insists on 0. I would appreciate if you could explain.
-1 appears to be correct. I just tried it, and an fgetc after fseek(f, -1, SEEK_END) produced the last character of the file. Seeking to 0 relative to SEEK_END resulted in fgetc returning EOF.
Clearly pointing to the last byte of a file is different from pointing to the end of the file. Furthermore, if there is any ambiguity in terminology, it is a failure of the test, not of the student. If the student has demonstrated the expected knowledge, they should receive credit.
When I use SEEK_END, I expect it to correspond to the position of the last character in the stream. But inside a printf it seems like it does not.
I have a file that contains 1010 characters. I open it with fopen, then I try to print the position of the last character of this file:
printf("Last position is: %d\n", SEEK_END);
The output is Last position is: 2 whereas I expect it to be Last position is: 1010. 2 does not correspond to the last line or last column, since I have 101 lines of 10 characters each. I don't know what it is.
Curiously, this code works well:
fseek(file, 0, SEEK_END);
printf("Last position is: %d\n", ftell(file));
The output is Last position is: 1010. But the problem here is that I am moving the virtual cursor in the file, which I do not want.
How can I print the value of SEEK_END without having to change the position of the virtual cursor, and more importantly, why do my printf outputs 2?
You have a misunderstanding. SEEK_END is not a magical symbol that somehow stores the length of the file you're thinking of right now. It is a constant, used as a code to tell fseek() what reference point to use for the specified file offset.
If you want to determine the size of a file whose name you know, and you are on a POSIX system, then you can use fstat(). If all you have is a FILE *, then your best bet is probably to record the current position, seek to the end of the stream, determine the position, and then seek back to the starting point:
fpos_t current;
long end;
/* error checks omitted for brevity */
fgetpos(file, ¤t);
fseek(file, 0, SEEK_END);
end = ftell(file);
fsetpos(file, ¤t);
printf("Last position is: %ld\n", end);
Of course, all that depends on the stream being seekable. Not all of them are. If you have a non-seekable stream then very likely there is no meaningful sense of its last position to begin with.
There is also a potential issue with file offsets exceeding the size of a long. fgetpos() and fsetpos() can be expected not to be sensitive to that, but their accompanying fpos_t type may be an aggregate type (i.e. a struct), and thus not direct interrogable. You can work around that with lseek(), but that comes from POSIX, not standard C.
SEEK_END is a parameter to fseek, it tells the fseek function how it should position in the file. You see 2 probably because in stdio.h it says #define SEEK_END 2 although the actual value is unspecified.
In order to get the offset you need to use ftell().
You can have a look at a specific implementation of fseek here.
From here is possible to see that SEEK_END is an integer constant. It is not like a variable that holds the last position of your file. So, whenever you print SEEK_END value, it will be 2.
I'm working with a binary format.
I've noticed that
fseek(fp, offset, SEEK_SET);
fread(&mystruct, sizeof(struct mystruct_thing), 1, fp);
produces output that's different from simply
fread(&mystruct, sizeof(struct mystruct_thing), 1, fp);
which follows expected behavior.
Why is this the case? Is it because SEEK_SET overrides the offset parameter?
The second argument of fread is the size of each item to be read, in this case the struct. I don't understand how you would expect the offset to go there – it should be something like sizeof(mystruct).
Edit: Now that the question has been edited, the reason why the two pieces of code produce different results is simply that the first one seeks the position of fp to offset before reading and the second one doesn't. fread reads sizeof(struct mystruct_thing) bytes starting from the current position of fp, so the starting position differs (assuming offset is not the same position at which you are already at before fseek) because fseek sets the position for future reads (and writes).
The first fragment will read a struct from offset bytes into the file, the second fragment will read it from the current file position - if the file has just been opened, that will be zero.
The obvious explanation perhaps is that offset is not equal to zero.
How can I move the file position back x bytes in C?
I don't want to use fseek(), because I'm reading lines of the file with open(fd, buf, buflength), not fopen().
I'd very much like to avoid reading the entire file into memory.
What I want to do is basically:
Read X + A = Y bytes.
Do something with the first X bytes.
Move the file pointer backwards A bytes.
Read Y bytes from there.
I know how to do everything except step 3. Anyone know how?
According to http://rabbit.eng.miami.edu/info/functions/unixio.html you could use lseek:
int lseek(int fd, int position, int startpoint)
include: <unistd.h>
Sets the file position effective for the next read or write operation.
fd = file descriptor as returned by open
position = position within file:
number of bytes between startpoint and the desired position, may be negative.
startpoint = location in file that position is relative to, one of:
SEEK_SET Position is number of bytes from beginning of file
SEEK_END Position is number of bytes after end of file
SEEK_CUR Position is number of bytes after current position
returns <0 for error,
or resulting file position relative to beginning of file.
Moving beyond the end of the file is permitted; the file is extended in length
only if a write occurs beyond the current end of file. Moving before the
beginning of a file is not permitted. A write operation after a move only
overwrites the number of bytes actually written.
I know my file pointer is at end of the line after printing this string: "xyz".
How can I get it to the start of the line? (pointing to x)
offset = ftell(fp);
fseek(fp, offset - sizeof("xyz") , SEEK_SET);
Above doesn't seem to work.
How can I achieve that?
I would store the offset by issuing a beginning = ftell(fp) before reading/writing you "xyz".
Then fseek(fp, beginning, SEEK_SET);
Would this be possible?
sizeof("xyz") will return 4 since you have the three characters plus the terminating null. You should use strlen("xyz") instead or subtract one from the sizeof result to account for the null.
As the type of "xyz" is char const *, sizeof("xyz") will return the size of a standard pointer, typically 4 or 8.
Also note that fseek does not work in text mode, only if the file has been opened in binary mode, as it's not possible to tell how big newlines are on the underlying host system.
In addition, it's better to use SEEK_CUR, as it will more the read/write point relative to the current position.