10

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?

Dan
  • 1,198
  • 4
  • 17
  • 34
  • 1
    You read into `length` and `count`, how have you defined them? Or, were they meant to be `hdr.length` and `hdr.count`? – Chris McGrath Mar 05 '14 at 08:37
  • 1
    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. – WhozCraig Mar 05 '14 at 11:38
  • @ChrisMcGrath Sorry, that was a copy-paste error, fixed it now – Dan Mar 05 '14 at 17:35
  • @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. – Dan Mar 05 '14 at 17:38
  • 2
    @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. – WhozCraig Mar 05 '14 at 18:10
  • @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 Mar 05 '14 at 23:46
  • 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. – Dan Mar 06 '14 at 08:04
  • 1
    @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 Mar 06 '14 at 09:53

2 Answers2

11

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

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

Marcel Zebrowski
  • 947
  • 10
  • 17
  • 3
    With this method, care must be taken to avoid memory alignment padding. See [Structure padding and packing](https://stackoverflow.com/q/4306186) – Jean-Christophe Jul 24 '20 at 04:55
9

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;
}
Blaz Bratanic
  • 2,279
  • 12
  • 17