Go to a certain point of a binary file in C (using fseek) and then reading from that location (using fread)
Asked Answered
A

3

8

I am wondering if this is the best way to go about solving my problem.

I know the values for particular offsets of a binary file where the information I want is held...What I want to do is jump to the offsets and then read a certain amount of bytes, starting from that location.

After using google, I have come to the conclusion that my best bet is to use fseek() to move to the position of the offset, and then to use fread() to read an amount of bytes from that position.

Am I correct in thinking this? And if so, how is best to go about doing so? i.e. how to incorporate the two together.

If I am not correct, what would you suggest I do instead?

Many thanks in advance for your help.

Matt

Edit:

I followed a tutorial on fread() and adjusted it to the following:

    `#include <stdio.h>
    int main()
    {
      FILE *f;
      char buffer[11];
      if (f = fopen("comm_array2.img", "rt"))
      {
        fread(buffer, 1, 10, f);
        buffer[10] = 0;
        fclose(f);
        printf("first 10 characters of the file:\n%s\n", buffer);
      }
      return 0;
    }`

So I used the file 'comm_array2.img' and read the first 10 characters from the file.

But from what I understand of it, this goes from start-of-file, I want to go from some-place-in-file (offset)

Is this making more sense?

Edit Number 2:

It appears that I was being a bit dim, and all that is needed (it would seem from my attempt) is to put the fseek() before the fread() that I have in the code above, and it seeks to that location and then reads from there.

Abbate answered 25/3, 2012 at 18:25 Comment(12)
Yes. Use fseek() and fread(). What code have you tried?Suchlike
Yes, you're right, but what is your question or problem?Heyde
There are tutorials on the web which show how to use them. Using the search terms "fseek fread C tutorial", it should be straightforward to find some. Have you worked through any?Denys
Look up the pread() system call. Emulate it using streams instead of file descriptors. Perhaps you write yourself a function such as: ssize_t fpread(char *buffer, size_t nbytes, size_t offset, FILE *fp);.Cardio
If your platform supports it, you could use pread() instead, which does both operations at once.Korykorzybski
Or use pread. Tell us more about what you want and what you've tried.Pall
ok, pread sounds like it is worth a look. I am using Linux (Ubuntu to be precise). I will add further detailAbbate
After looking at pread() it certainly looks like what I am after, thank you for suggesting that :)Abbate
This is the weirdest question. You already say in your question that you need fseek(), yet for reasons unknown you refuse to use it in your code or even look it up in the manual to see how it could be used.Heyde
I have looked at the man pages for both fseek() and fread() but I am rather new to this and the thing that I am unsure of is how to incorporate the two together, I tried googling how to use the two in conjunction with one-another but to no avail (probably down to my lack of googling skills?) and so I guess what I am after is a simple breakdown of how to use the two together. I was going to trial and error a bit, but while I was going so, I thought I would put my question out there. I apologise if my question seems odd or off in some way, this is my first question put to this site.Abbate
Open the file in binary mode: fopen("comm_array2.img", "rb"). The fseek() calls may give strange results when applied to a file opened in text mode. Oh ... the flag "t" is not recognized by the Standard: it's the absence of a "b" that makes the file a text mode one.Across
@Across thank you for the advice about binary mode, I missed that.Abbate
C
3

If you are using file streams instead of file descriptors, then you can write yourself a (simple) function analogous to the POSIX pread() system call.

You can easily emulate it using streams instead of file descriptors1. Perhaps you should write yourself a function such as this (which has a slightly different interface from the one I suggested in a comment):

size_t fpread(void *buffer, size_t size, size_t mitems, size_t offset, FILE *fp)
{
     if (fseek(fp, offset, SEEK_SET) != 0)
         return 0;
     return fread(buffer, size, nitems, fp);
}

This is a reasonable compromise between the conventions of pread() and fread().


What would the syntax of the function call look like? For example, reading from the offset 732 and then again from offset 432 (both being from start of the file) and filestream called f.

Since you didn't say how many bytes to read, I'm going to assume 100 each time. I'm assuming that the target variables (buffers) are buffer1 and buffer2, and that they are both big enough.

if (fpread(buffer1, 100, 1, 732, f) != 1)
    ...error reading at offset 732...
if (fpread(buffer2, 100, 1, 432, f) != 1)
    ...error reading at offset 432...

The return count is the number of complete units of 100 bytes each; either 1 (got everything) or 0 (something went awry).

There are other ways of writing that code:

if (fpread(buffer1, sizeof(char), 100, 732, f) != 100)
    ...error reading at offset 732...
if (fpread(buffer2, sizeof(char), 100, 432, f) != 100)
    ...error reading at offset 432...

This reads 100 single bytes each time; the test ensures you got all 100 of them, as expected. If you capture the return value in this second example, you can know how much data you did get. It would be very surprising if the first read succeeded and the second failed; some other program (or thread) would have had to truncate the file between the two calls to fpread(), but funnier things have been known to happen.


1 The emulation won't be perfect; the pread() call provides guaranteed atomicity that the combination of fseek() and fread() will not provide. But that will seldom be a problem in practice, unless you have multiple processes or threads concurrently updating the file while you are trying to position and read from it.

Cardio answered 25/3, 2012 at 19:57 Comment(6)
Thank you for this, I was about to ask you to go into your comment a little further and you preempted me, so thanks. So if I am understanding this right, I would create that function, and use it in the same was as I would have originally used pread and pass it the filename (instead of file descriptor) and offset?Abbate
Sort of...Your comment mentions file name, but neither fread() nor fseek() works with file names; that is fopen()'s job. The fpread() function is an almost drop-in replacement for the fseek() and fread() calls. Clearly, if you want it to take a file name, you have to fopen() and (presumably) fclose() the file inside the function, and you won't be passing the fp parameter. You'd probably put the filename as the first argument. You'd use "rb" to open the binary file for reading; it doesn't matter whether the b is there on Unix, but on other platforms, it does.Cardio
Ah yes, my apologies I meant to say pass what would be 'fp' (i.e. what is normally returned when you use fopen() as I understand it) However, from your comment I see that I won't in fact pass the 'fp' parameter?Abbate
The code shown expects the FILE *; I would expect you to pass the file stream. (I was confused/misled by your first comment.) You could write a function to open, position, read, close a given file name, and then you would pass a file name instead of a file stream. But, if you're going to read more than one section of the file at a time, I would not recommend it; fopen() is a relatively slow function, and should be called no more often than necessary. So, given your original comment meant file stream instead of file name, then yes: you could use fpread() as you wanted.Cardio
Ok, thank you very much for your help and clarification. One final thing so that I can fully understand your function you have created, what would the syntax of the function call look like? For example, reading from the offset 732 and then again from offset 432 (both being from start of the file) and filestream called 'f'. I apologise if this seems like an easy question, but I want to make sure I understand this fully. Thank you again for your time, it is much appreciated.Abbate
Thank you so much! This has been incredibly helpful, and much appreciated.Abbate
S
1

It frequently depends on the distance between the parts you care about. If you're only skipping over/ignoring a few bytes between the parts you care about, it's often easier to just read that data and ignore what you read, rather than using fseek to skip past it. A typical way to do this is define a struct holding both the data you care about, and place-holders for the ones you don't care about, read in the struct, and then just use the parts you care about:

struct whatever {
   long a;
   long ignore;
   short b;
} w;

fread(&w, 1, sizeof(w), some_file);

// use 'w.a' and 'w.b' here.

If there's any great distance between the parts you care about, though, chances are that your original idea of using fseek to get to the parts that matter will be simpler.

Salable answered 25/3, 2012 at 18:45 Comment(0)
M
0

Your theory sounds correct. Open, seek, read, close.

Create a struct to for the data you want to read and pass a pointer to read() of struct's allocated memory. You'll likely need #pragma pack(1) or similar on the struct to prevent misalignment problems.

Matland answered 25/3, 2012 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.