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.
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.
I am making a program that enables the user to edit a text file. One of the available options is "move". When selected, the program asks for a number of character to skip, and it skips them in the file, using fseek.
However to do that I need to ensure that the position the user tries to reach corresponds to an actual character. To do so, I have created a function that checks if the skipping is possible.
This is how I do:
I save the position in the file, as pos.
I use fseek to get the position of the last character in the file.
I compare this position to the position corresponding to the sum of pos and the number of characters that the user wants to skip.
Whether the result is true or false, the position in the file is always reset to its initial value, pos, using fseek.
This is my original code:
if (pos + nb > ftell(file))
{
fseek(file, 0, pos);
return (1);
}
else
{
fseek(file, 0, pos);
return (0);
}
With this code, if the initial value, pos, is not 0, fseek will not reset the position to pos, but to the position of the last character. (?!) I found out that replacing the order of the arguments and passing pos as the offset and 0 as the starting position makes it work. But I can't get why the previous code doesn't work.
I would like some help to understand better how the function fseek works, there are, obviously, some details that I fail to find. I found nothing about "fseek" in the GNU C reference manual...
You are using fseek() wrong, try
fseek(file, pos, SEEK_SET);
always read the (documentation or specification), it will save you a lot of problems.
I found out that replacing the order of the arguments and passing pos as the offset and 0 as the starting position makes it work. But I can't get why the previous code doesn't work
Well, it's because that's the right order for the parameters, and incidentally 0 is the value of SEEK_SET.
Also, you need to be careful, you say number of characters and that is not the same as number of bytes, for instance in UTF-8 number of bytes for character encoding varies from one character to another. According to the fseek() specification, it skips pos bytes, not characters.
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.
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.