c++11binaryfilesifstream

Problems when reading binary files in C++


Here is a code to generate a binary file "input" :

#include <fstream>
int main() {
    unsigned char buf[] = {
        0x06, 0x00, 0x00, 0x00, 0x62, 0x6f, 0x79, 0x31, 0x00, 0x00, 0x00, 0x00,
        0x6d, 0x0f, 0x00, 0x00, 0xc8, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x62, 0x6f,
        0x79, 0x32, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x10, 0x00, 0x00, 0xa0, 0x42,
        0x9a, 0x99, 0xd9, 0x3f, 0x62, 0x6f, 0x79, 0x33, 0x00, 0x00, 0x00, 0x00,
        0x6d, 0x11, 0x00, 0x00, 0x70, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x62, 0x6f,
        0x79, 0x34, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x12, 0x00, 0x00, 0x20, 0x42,
        0x9a, 0x99, 0xd9, 0x3f, 0x67, 0x69, 0x72, 0x6c, 0x31, 0x00, 0x00, 0x00,
        0x66, 0x13, 0x00, 0x00, 0x48, 0x42, 0x9a, 0x99, 0xd9, 0x3f, 0x67, 0x69,
        0x72, 0x6c, 0x32, 0x00, 0x00, 0x00, 0x66, 0x13, 0x00, 0x00, 0x48, 0x42,
        0x9a, 0x99, 0xd9, 0x3f};
    std::ofstream fout("input", std::ofstream::binary);
    fout.write((char *)buf, sizeof(buf));
    fout.close();
    return 0;
}

The details are as : The first 0x06 indicates how many records are there, and the follows are some records, each with name, gender('m' or 'f'), age, weight and height.

To read this binary file in c++, I created a struct :

struct Person{
    unsigned char name[8];
    unsigned char gender;
    unsigned int age;
    float weight,height;
}person[100];

And the code fragment below is how I read this file :

using namespace std;
ifstream fin;
fin.open("input",ios::binary);

int n;
fin.read(reinterpret_cast<char*>(&n),sizeof(n));

for(int i=0;i<n;i++)
     fin.read(reinterpret_cast<char*>(&person[i]),sizeof(person[i]));

and I try to make an ouput, but then it appears garbled codes :

code:
for(int i=0;i<n;i++)
     cout<<person[i].name<<' '<<person[i].gender<<' '<<person[i].age<<' '<<person[i].weight<<' '<<person[i].height<<endl;

output:
boy1  2577023688 7.00208e+28 1.81062e-41
 � 863596386 0 6.25119e-42
pB���?boy4  309133312 40 1.7
girl1  2577023560 1.74727e+25 4.63068e-39
 � 0 0 0
  0 0 0

and this makes me really comfused. Can someone explain why this will happen and how to solve it?


Solution

  • You have several problems.

    First, the name is 8 characters, not 10. Second, the age is only one byte, not 4 bytes. Third, because you have mixed types, you are getting padding within the structure that messes things up. You need to declare it as "packed":

    #include <iostream>
    #include <fstream>
    using namespace std;
    
    struct Person{
        char name[8];
        char gender;
        unsigned char age;
        float weight,height;
    } __attribute__((packed)) person[100];
    
    int main()
    {
        ifstream fin;
        fin.open("input",ios::binary);
    
        int n;
        fin.read(reinterpret_cast<char*>(&n),sizeof(n));
        cout << "Reading " << n << " records\n";
        cout << "Each is " << sizeof(Person) << " bytes\n";
    
        for(int i=0;i<n;i++)
            fin.read(reinterpret_cast<char*>(&person[i]),sizeof(person[i]));
    
        for(int i=0;i<n;i++)
            cout<<person[i].name<<" "<<person[i].gender<<" "<<(int)person[i].age<<" "<<person[i].weight<<" "<<person[i].height<<endl;
    }
    

    Output

    Reading 6 records
    Each is 18 bytes
    boy1 m 15 100 1.7
    boy2 m 16 80 1.7
    boy3 m 17 60 1.7
    boy4 m 18 40 1.7
    girl1 f 19 50 1.7
    girl2 f 19 50 1.7
    

    In Windows, you'd use #pragma packed(1) instead of the attribute.