Move the file position without fseek in C - c

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.

Related

how to tell fread and fwrite where to go read from or write into a file

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.

C: SEEK_END in a printf shows unexpected value

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, &current);
fseek(file, 0, SEEK_END);
end = ftell(file);
fsetpos(file, &current);
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.

behaviour of fseek and SEEK_END

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.

Why the ftell returns 0 in this function?

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.

C: Reading file with a starting point

A simple question but I can't find the answer in my book. I want to read a binary file to seed a random number generator, but I don't want to seed my generator with the same seed each time I call the function, so I will need to keep a variable for my position in the file (not a problem) and I would need to know how to read a file starting a specific point in the file (no idea how). The code:
void rng_init(RNG* rng) {
// ...
FILE *input = fopen("random.bin", "rb");
unsigned int seed[32];
fread(seed, sizeof(unsigned int), 32, input);
// seed 'rng'...
fclose(input);
}
You can use ftell() to read the current position of the file, and fseek() to jump to a specific position, e.g.
long cur = ftell(f);
fseek(f, 0, SEEK_START); // jump to beginning
fread(...)
fseek(f, cur, SEEK_START); // returning to previous location.
You can use fseek to move to a random position within a file.
fseek takes a third parameter that tells what the position is relative to.
SEEK_SET - the absolute position from the start of the file
SEEK_CUR - the position relative to where you currently are in the file
SEEK_END - the position relative to the end of the file
Just fseek before you read anything!

Resources