There are three origin constants you can use in functions like fseek to determine from where your offset is counted: SEEK_SET, SEEK_CUR, and SEEK_END. SEEK_CUR and SEEK_END seem self-explanatory to mean the current position and end of the file stream, but why is SEEK_SET used to mean the beginning? Why not something like SEEK_BEG?
Because you can add an offset. By using SEEK_SET, you can explicitly set an offset. (By adding it to the beginning)
From the manpage of fseek:
The new position, measured in bytes, is
obtained by adding offset bytes to the position specified by whence.
If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is
relative to the start of the file, the current position indicator, or
end-of-file, respectively.
From the manpage of lseek:
SEEK_SET
The file offset is set to offset bytes.
SEEK_CUR
The file offset is set to its current location plus offset
bytes.
SEEK_END
The file offset is set to the size of the file plus offset
bytes.
Another answer to the question as stated is "Because fseek has a second argument which isn't always zero".
If you always passed the second argument as zero, then SEEK_CUR would set the file pointer to its current position (which would be a nearly useless no-op), and SEEK_END would set the file pointer to the end of file, and SEEK_CUR would set it to the beginning of the file, which might make you wonder why it wasn't called SEEK_BEG.
But of course fseek does have that second argument, and you usually pass it as an interesting, non-zero offset. Much of the time, the second argument is the absolute offset you want to seek to, which is what SEEK_SET means. As a convenience, you can also set a position plus-or-minus the current position, which is what SEEK_CUR is for, or plus-or-minus the end of the file, which is what SEEK_END is for.
In the case that whence is SEEK_SET and the offset is 0, meaning that you're trying to set the file pointer to the beginning of the file, there maybe ought to be a convenient shortcut for that, too. But the shortcut isn't called SEEK_BEG, it's a completely different library function: rewind(fp), which is indeed a shortcut for fseek(fp, 0L, SEEK_SET).
Related
I am learning to use fread and fwrite right now.
As far as I understand, from the documentation, it just seems to read from or write into a specified number of bytes, but always from the beginning of the file. Is there any way not to have to start at the beginning of the file or am I misunderstanding the functions?
use fseek
int fseek(FILE *pointer, long int offset, int position)
pointer: pointer to a FILE object that identifies the stream.
offset: number of bytes to offset from position
position: position from where offset is added.
returns:
zero if successful, or else it returns a non-zero value
SEEK_END : It denotes end of the file.
SEEK_SET : It denotes starting of the file.
SEEK_CUR : It denotes file pointer’s current position.
I'm new to C and am using fseek. My question is, what exactly does it do?
eg: fseek(FILE *stream, -1, SEEK_CUR) will read 1 character backwards, so is it already at the end of the file?
Does that mean fseek(FILE *stream, 0, SEEK_CUR) does nothing?
And how do we apply the functions after fseek to a data structure?
I'm new to C and am using fseek. My question is, what exactly does it do?
fseek sets the file position indicator for the stream pointed to by stream. The new position will be given by the offset in conjunction with the flag:
For SEEK_SET, the offset is relative to the start of the file.
For SEEK_CUR the offset is relative to the current indicator position.
For SEEK_END it's relative to end-of-file.
eg: fseek(FILE *stream, -1, SEEK_CUR) will read 1 character backwards, so is it already at the end of the file?
It may be at beginning, in the middle or at the end, the current position is where it is right now. If the indicator is at the end of the file it will be moved one byte back.
It does not read characters, as stated, it positions the indicator to the desired position, by offset, from it's current position as explained above.
Does that mean fseek(FILE *stream, 0, SEEK_CUR) does nothing?
In practical terms it does nothing visible, because the offset is 0 it does not move the indicator, it just places it at the same position. It does clear the end-of-file status (resets the EOF flag as mentioned by #rici).
From C11 N1570 §7.21.9.2
#include <stdio.h>
int fseek(FILE *stream, long int offset, int whence);
Description:
2 The fseek function sets the file position indicator for the stream pointed to by stream. If a read or write error occurs, the error indicator for the stream is set and fseek fails.
3 For a binary stream, the new position, measured in characters from the beginning of the file, is obtained by adding offset to the position specified by whence. The specified position is the beginning of the file if whence is SEEK_SET, the current value of the file position indicator if SEEK_CUR, or end-of-file if SEEK_END. A binary stream need not meaningfully support fseek calls with a whence value of SEEK_END.
4 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.
5 After determining the new position, a successful call to the fseek function undoes any effects of the ungetc function on the stream, clears the end-of-file indicator for the stream, and then establishes the new position. After a successful fseek call, the next operation on an update stream may be either input or output.
Returns:
6 The fseek function returns nonzero only for a request that cannot be satisfied.
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.
When I run my program and I choose to see the product list, it doesn't print anything. After some time, I find out that the value of fl_size is always 0. Why is this?
void view_prdct_code_list() {
FILE *stock = fopen("stock.dat","r+");
assert(stock);
int fl_size=ftell(stock);
int prd_size= sizeof(product);
int quantity= fl_size/prd_size;
printf("fl_size=%d",fl_size);
fseek(stock,0,SEEK_SET);
prdct cprd= (product *)malloc (sizeof(product)*quantity);
assert(cprd);
int i;
fread(cprd,prd_size,quantity,stock);
for (i=0;i<quantity;i++){
printf("PRODUCT CODE: %d\n",cprd->code);
}
free(cprd);
fclose(stock);
}
ftell does not return the total size of the file; it returns the current read or write position within the file. You call ftell immediately after opening the file, so that position is the very beginning of the file. You can either use fseek(stock, 0, SEEK_END) to seek to the end before calling ftell, or you can drop down a layer and use fstat(fileno(stock)) to retrieve the file size directly from the OS.
Further notes:
Neither of these options will work if you are reading from a pipe. (In general, you need to check for the success of every single one of your file access operations.)
fread is not guaranteed to read the entire file in one gulp even if you ask it to.
As 'alk' points out, ftell returns a long, not an int.
If long is only 32 bits, it is possible for a file to be so large that its size does not fit in a long. If your program needs to handle files that big, you need to #define _FILE_OFFSET_BITS 64 at the top of every .c file (before all the includes) and use fseeko and ftello. (Or whatever the Windows equivalent is.)
You should be opening this apparently-binary file with mode "r+b".
Binary files with no file header (and in particular with no magic number, of at least four bytes, at offset zero) are a Bad Thing.
Do not cast the return value of malloc. (It is necessary to do this in C++, but in C it is not only unnecessary, it can hide bugs.)
Check man page for ftell, for example this one: http://linux.die.net/man/3/ftell
Here is the relevant part: "The ftell() function obtains the current value of the file position indicator for the stream pointed to by stream."
When you open the file, cursor position will be at the start. So distance from start will be zero. Therefore ftell returns zero.
To find the size of file, see this link: How can I get a file's size in C?. Here's a snippet in short:
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
Make sure to call fseek(fp, 0L, SEEK_SET); after above.
Because ftell returns the size from the beginning to the current position of the file.
fseek(stock,0,SEEK_SET);
Means you set the position to the first bite of the file.
Also you gotta set fl_size=ftell(stock); after the fseek.