读取二进制文件到一个结构(C ++)(Reading Binary File into a Stru

2019-08-17 20:13发布

所以我有一点不能够正确读取二进制文件到我的结构的问题。 该结构是这样的:

struct Student
{
    char name[25];
    int quiz1;
    int quiz2;
    int quiz3;
};

它是37个字节(从字符阵列25个字节,并且每整数4个字节)。 我的.dat文件是185个字节。 这5名学生有3个整数等级。 因此,每个学生占用37个字节(37×5 = 185)。

它看起来以纯文本格式是这样的:

Bart Simpson          75   65   70
Ralph Wiggum          35   60   44
Lisa Simpson          100  98   91
Martin Prince         99   98   99
Milhouse Van Houten   80   87   79

我能单独使用此代码来读取每个记录:

Student stud;

fstream file;
file.open("quizzes.dat", ios::in | ios::out | ios::binary);

if (file.fail())
{
    cout << "ERROR: Cannot open the file..." << endl;
    exit(0);
}

file.read(stud.name, sizeof(stud.name));
file.read(reinterpret_cast<char *>(&stud.quiz1), sizeof(stud.quiz1));
file.read(reinterpret_cast<char *>(&stud.quiz2), sizeof(stud.quiz2));
file.read(reinterpret_cast<char *>(&stud.quiz3), sizeof(stud.quiz3));

while(!file.eof())
{
    cout << left 
         << setw(25) << stud.name
         << setw(5)  << stud.quiz1
         << setw(5)  << stud.quiz2
         << setw(5)  << stud.quiz3
         << endl;

    // Reading the next record
    file.read(stud.name, sizeof(stud.name));
    file.read(reinterpret_cast<char *>(&stud.quiz1), sizeof(stud.quiz1));
    file.read(reinterpret_cast<char *>(&stud.quiz2), sizeof(stud.quiz2));
    file.read(reinterpret_cast<char *>(&stud.quiz3), sizeof(stud.quiz3));
}

我得到一个很好看的输出,但我希望能够在同一时间在同一时间在一个整体结构看,每个结构的不只是个人会员。 此代码是相信什么需要我来完成任务,但是......它不工作(我会告诉后输出):

*不包括类似的部分尽可能的文件和结构声明的开度等

file.read(reinterpret_cast<char *>(&stud), sizeof(stud));

while(!file.eof())
{
    cout << left 
         << setw(25) << stud.name
         << setw(5)  << stud.quiz1
         << setw(5)  << stud.quiz2
         << setw(5)  << stud.quiz3
         << endl;

    file.read(reinterpret_cast<char *>(&stud), sizeof(stud));
}

OUTPUT:

Bart Simpson             16640179201818317312
ph Wiggum                288358417665884161394631027
impson                   129184563217692391371917853806
ince                     175193530917020655191851872800

唯一的部分它不会弄乱是第一个名字,在那之后它下山。我已经试过一切,我不知道什么是错的。 我甚至通过本书我有搜查,我找不到任何东西。 在那里的东西是什么样子我已经和他们的工作,但由于某种奇怪的原因,我的没有。 我做了file.get(CH)(CH是一个字符)的字节25和它返回K,这是ASCII 75 ..这是第一次测试成绩,所以,一切都在它应该是。 它只是没有在我的阅读结构正常。

任何帮助将不胜感激,我只是坚持了这一点。

编辑:从你们那里接收如此大量的意外,真棒输入后,我决定接受你的建议,并在同一时间内的一个成员阅读坚持。 我做的事情,通过使用功能更清洁和更小的。 再次感谢您提供这样的快速和启发性的输入。 它的大加赞赏。

如果你感兴趣的是不建议大多数一种变通方法,滚动向底部,由user1654209第三的答案。 这解决方法完美的作品,但阅读所有的意见,看看为什么它不看好。

Answer 1:

你的结构几乎肯定被填充,以保持其内容的对齐方式。 这意味着它不会是37个字节,而且不匹配会导致读数不同步。 看着每个串失去3个字符的方式,似乎已经填充到40个字节。

由于填充可能是字符串和整数之间,甚至不是第一个记录正确读取。

在这种情况下,我会建议不要试图读取你的数据作为二进制BLOB,并坚持阅读各个字段。 这是更强大的,特别是如果你甚至想改变你的结构。



Answer 2:

没有看到写入数据的代码,我猜你写的数据,你在第一个例子读取的方式,每个元素一个接一个。 然后在文件中的每条记录的确会37个字节。

然而,由于编译器垫结构,穿上漂亮的边界成员优化的原因,你的结构是40个字节。 所以,当你在一个单一的通话阅读完整的结构,那么你实际上是在一个时间,这意味着你的阅读会走出相在文件中实际记录读取40个字节。

您可能已重新实现文字写在一气呵成的完整结构,或者使用的地方读取你每次读一个成员领域的第一个方法。



Answer 3:

一个简单的解决方法是将您的结构收拾1个字节

使用gcc

struct __attribute__((packed)) Student
{
    char name[25];
    int quiz1;
    int quiz2;
    int quiz3;
};

使用MSVC

#pragma pack(push, 1) //set padding to 1 byte, saves previous value
struct  Student
{
    char name[25];
    int quiz1;
    int quiz2;
    int quiz3;
};
#pragma pack(pop) //restore previous pack value

编辑:由于用户ahans指出:编译包是由海湾合作委员会自2.7.2.3版本(1997年发布)的支持,因此似乎可以安全地使用编译包作为唯一的包装符号,如果你靶向MSVC和gcc



Answer 4:

正如你已经发现,填充是这里的问题。 此外,正如其他人建议,解决这个的正确方法是逐个读取每个成员作为你在你的例子做。 我不希望这个费用比一次性能明智的阅读整个事情更多。 但是,如果你仍然想继续前进,因为一次读它,你可以告诉编译器做不同的填充:

#pragma pack(push, 1)
struct Student
{
    char name[25];
    int quiz1;
    int quiz2;
    int quiz3;
};
#pragma pack(pop)

随着#pragma pack(push, 1)你告诉编译器保存到内部栈当前数据组值,然后使用1包价值。 这意味着你得到1个字节,这意味着没有填充在所有在这种情况下的定位。 随着#pragma pack(pop)你告诉编译器从堆栈中最后一个值,然后利用这一点,从而恢复行为的定义之前使用的编译器struct

虽然#pragma通常表明非便携,编译器相关功能,这一个工程至少与海湾合作委员会和微软VC ++。



Answer 5:

还有就是要解决这个线程问题的方法不止一种。 这是基于使用结构和炭BUF的联合解决方案:

#include <fstream>
#include <sstream>
#include <iomanip>
#include <string>

/*
This is the main idea of the technique: Put the struct
inside a union. And then put a char array that is the
number of chars needed for the array.

union causes sStudent and buf to be at the exact same
place in memory. They overlap each other!
*/
union uStudent
{
    struct sStudent
    {
        char name[25];
        int quiz1;
        int quiz2;
        int quiz3;
    } field;

    char buf[ sizeof(sStudent) ];    // sizeof calcs the number of chars needed
};

void create_data_file(fstream& file, uStudent* oStudent, int idx)
{
    if (idx < 0)
    {
        // index passed beginning of oStudent array. Return to start processing.
        return;
    }

    // have not yet reached idx = -1. Tail recurse
    create_data_file(file, oStudent, idx - 1);

    // write a record
    file.write(oStudent[idx].buf, sizeof(uStudent));

    // return to write another record or to finish
    return;
}


std::string read_in_data_file(std::fstream& file, std::stringstream& strm_buf)
{
    // allocate a buffer of the correct size
    uStudent temp_student;

    // read in to buffer
    file.read( temp_student.buf, sizeof(uStudent) );

    // at end of file?
    if (file.eof())
    {
        // finished
        return strm_buf.str();
    }

    // not at end of file. Stuff buf for display
    strm_buf << std::setw(25) << std::left << temp_student.field.name;
    strm_buf << std::setw(5) << std::right << temp_student.field.quiz1;
    strm_buf << std::setw(5) << std::right << temp_student.field.quiz2;
    strm_buf << std::setw(5) << std::right << temp_student.field.quiz3;
    strm_buf << std::endl;

    // head recurse and see whether at end of file
    return read_in_data_file(file, strm_buf);
}



std::string quiz(void)
{

    /*
    declare and initialize array of uStudent to facilitate
    writing out the data file and then demonstrating
    reading it back in.
    */
    uStudent oStudent[] =
    {
        {"Bart Simpson",          75,   65,   70},
        {"Ralph Wiggum",          35,   60,   44},
        {"Lisa Simpson",         100,   98,   91},
        {"Martin Prince",         99,   98,   99},
        {"Milhouse Van Houten",   80,   87,   79}

    };




    fstream file;

    // ios::trunc causes the file to be created if it does not already exist.
    // ios::trunc also causes the file to be empty if it does already exist.
    file.open("quizzes.dat", ios::in | ios::out | ios::binary | ios::trunc);

    if ( ! file.is_open() )
    {
        ShowMessage( "File did not open" );
        exit(1);
    }


    // create the data file
    int num_elements = sizeof(oStudent) / sizeof(uStudent);
    create_data_file(file, oStudent, num_elements - 1);

    // Don't forget
    file.flush();

    /*
    We wrote actual integers. So, you cannot check the file so
    easily by just using a common text editor such as Windows Notepad.

    You would need an editor that shows hex values or something similar.
    And integrated development invironment (IDE) is likely to have such
    an editor.   Of course, not always so.
    */


    /*
    Now, read the file back in for display. Reading into a string buffer
    for display all at once. Can modify code to display the string buffer
    wherever you want.
    */

    // make sure at beginning of file
    file.seekg(0, ios::beg);

    std::stringstream strm_buf;
    strm_buf.str( read_in_data_file(file, strm_buf) );

    file.close();

    return strm_buf.str();
}

呼叫测验()和接收格式显示到std ::法院,写入文件,或任何一个字符串。

其主要思想是,所有的工会里面的物品在内存中的同一地址开始。 所以,你可以有一个char或wchar_t的BUF,其大小如要写入或从文件中读取的结构相同。 并注意需要零次转换。 没有在代码中一个演员。

我也没有担心填充。

对于那些谁不喜欢递归,对不起。 工作它与递归对我来说更容易,更容易出错。 也许不是为别人更容易? 该递归可以转换为环路。 他们将需要被转换成循环的非常大的文件。

对于那些谁喜欢递归,这又是使用递归的另一个实例。

我不主张用工会是最好的解决办法还是不行。 似乎是一个解决方案。 也许你喜欢吗?



文章来源: Reading Binary File into a Structure (C++)