Reading binary data into struct with ifstream

31,215

Solution 1

As the comment above states, you are probably missing hdr.length and hdr.count. I tried it with gcc 4.8 and clang 3.5 and it works correctly.

#include <iostream>
#include <fstream>

#pragma pack(push, r1, 1)
struct Header {
    char id[15];
    int length;
    int count;
};
#pragma pack(pop, r1)

int main() {
  Header h = {"alalalala", 5, 10};

  std::fstream fh;
  fh.open("test.txt", std::fstream::out | std::fstream::binary);
  fh.write((char*)&h, sizeof(Header));
  fh.close();

  fh.open("test.txt", std::fstream::in | std::fstream::binary);

  fh.read((char*)&h.id, sizeof(h.id));
  fh.read((char*)&h.length, sizeof(h.length));
  fh.read((char*)&h.count, sizeof(h.count));

  fh.close();

  std::cout << h.id << " " << h.length << " " << h.count << std::endl;
}

Solution 2

It is also possible to read the struct in one step.

i.e. fh.read((char*)&h, sizeof(Header));

Share:
31,215
Dan
Author by

Dan

Updated on July 27, 2022

Comments

  • Dan
    Dan over 1 year

    I'm trying to read binary data from a file using ifstream.

    Specifically, I'm trying to populate this "Header" struct with data read from a file:

    struct Header {
        char id[16];
        int length;
        int count;
    };
    
    1. Now, if I read the file in this way, the result is exactly what I want:

      input.read((char*)&hdr, sizeof(hdr));
      
    2. But if I instead read each variable of the struct manually, the results are gibberish:

      input.read((char*)&hdr.id,     sizeof(hdr.id));
      input.read((char*)&hdr.length, sizeof(hdr.length));
      input.read((char*)&hdr.count,  sizeof(hdr.count));
      

    My question is, what is happening here that makes these two methods return different results?

    • Chris McGrath
      Chris McGrath about 10 years
      You read into length and count, how have you defined them? Or, were they meant to be hdr.length and hdr.count?
    • WhozCraig
      WhozCraig about 10 years
      If you think it relevant how you wrote the data to that file (or someone else did), you'd be right; it is so. Short answer: sizeof(Header) is required to be "at least" as big as the sum of the size of its members, and can be bigger when including implementation member padding. The members aren't guaranteed to be buttressed up against each other when writing the struct as a whole, yet you're reading it expecting they are. I.e. If you're "writing" a orange, trying to "read" a bag a tangerines isn't necessarily going to work.
    • Dan
      Dan about 10 years
      @ChrisMcGrath Sorry, that was a copy-paste error, fixed it now
    • Dan
      Dan about 10 years
      @WhozCraig Are you saying that the first method of reading the data works and the second doesn't because there is padding between the variables within the struct? I don't know how this particular data was written, I only know how it's supposed to be read, so it's curious to me that it was written in such a way that the first method of reading it works perfectly.
    • WhozCraig
      WhozCraig about 10 years
      @Dan You "only know how its supposed to be read." And how is that ? The first method assumes whoever wrote this did it with the same layout and padding that you're using. If there is no padding issue, then the second will work (and is nearly the preferred way to do this to begin with, both for reading and writing). Given the layout and size of the data members in that struct, it would be odd if there were padding between elements, after the last element, or both, but I've seen odder things. sizeof(hdr) compared to sizeof() each member will tell much.
    • Dan
      Dan about 10 years
      @WhozCraig Thanks, I'll look into the sizes of each and report back if I find anything weird. I did not write the file myself, so I don't know how it was written; and I only know how to read it because the code above is adapted from code someone else has given me - although strangely, they didn't write the file either, so I don't know how they figured it out. I just asked the question out of curiosity really, when I noticed that the 2 methods were behaving differently.
    • Dan
      Dan about 10 years
      The sizes match up... but it turns out my "copy-paste error" WAS actually present in the code, so that was the problem! The 2 methods now work identically :) Thanks for all your help.
    • WhozCraig
      WhozCraig about 10 years
      @Dan Don't count on it always being that way. You're fortunate neither padding nor endianess were an issue, because they usually are.
  • WhozCraig
    WhozCraig about 10 years
    Change the id field to char id[15] and run that again, just for kicks.
  • Blaz Bratanic
    Blaz Bratanic about 10 years
    You are right. Added pragma directives to fix alignment.
  • Jean-Christophe
    Jean-Christophe over 3 years
    With this method, care must be taken to avoid memory alignment padding. See Structure padding and packing
  • Jamie
    Jamie over 2 years
    This would be much improved by handling byte order. If this is written on a big endian system and read on a small endian system, things would go poorly.