简体   繁体   English

如何在 c++ 中使用二进制文件输入/输出读取/写入结构的字符串类型成员?

[英]How to read/write string type member of a struct using binary file in/out in c++?

I have 2 c++ code: one is for write data into a binary file, another is for read that file.我有 2 c++ 代码:一个用于将数据写入二进制文件,另一个用于读取该文件。

write.cpp code is as below: write.cpp代码如下:

#include <iostream>
#include <fstream>
using namespace std;
const int NAME_SIZE = 51;
struct Data
{
    char name[NAME_SIZE];
    int age;
};

int main()
{
    Data person;
    char again;

    fstream people("people.db", ios::out | ios::binary);

    do
    {
        cout << "Enter the following data about a "<< "person:\n";
        cout << "Name: ";
        cin.getline(person.name, NAME_SIZE);
        cout << "Age: ";
        cin >> person.age;
        cin.ignore();

        people.write(reinterpret_cast<char *>(&person),sizeof(person));

        cout << "Do you want to enter another record? ";
        cin >> again;
        cin.ignore();
    } while (again == 'Y' || again == 'y');
    people.close();



    return 0;
}

read.cpp code is as below: read.cpp代码如下:

#include <iostream>
#include <fstream>
using namespace std;
const int NAME_SIZE = 51;
struct Data
{
    char name[NAME_SIZE];
    int age;
};

int main()
{
    Data person;
    char again;
    fstream people;

    people.open("people.db", ios::in | ios::binary);

    if (!people)
    {
        cout << "Error opening file. Program aborting.\n";
        return 0;
    }

    cout << "Here are the people in the file:\n\n";
    people.read(reinterpret_cast<char *>(&person),sizeof(person));

    while (!people.eof())
    {
        cout << "Name: ";
        cout << person.name << endl;
        cout << "Age: ";
        cout << person.age << endl;

        cout << "\nPress the Enter key to see the next record.\n";
        cin.get(again);
        people.read(reinterpret_cast<char *>(&person),sizeof(person));
    }
    cout << "That's all the data in the file!\n";
    people.close();
    return 0;
}

Above mentioned codes work fine.上面提到的代码工作正常。 The problem arises when I use string type members in the structure:当我在结构中使用字符串类型成员时出现问题:

new write.cpp :新的write.cpp

#include <iostream>
#include <fstream>
using namespace std;

struct Data
{
    string name;
    int age;
};

int main()
{
    Data person;
    char again;

    fstream people("people.db", ios::out | ios::binary);

    do
    {
        cout << "Enter the following data about a "<< "person:\n";
        cout << "Name: ";
        cin>>person.name;
        cout << "Age: ";
        cin >> person.age;
        cin.ignore();

        people.write(reinterpret_cast<char *>(&person),sizeof(person));

        cout << "Do you want to enter another record? ";
        cin >> again;
        cin.ignore();
    } while (again == 'Y' || again == 'y');
    people.close();



    return 0;
}

new read.cpp :新的read.cpp

#include <iostream>
#include <fstream>
using namespace std;

struct Data
{
    string name;
    int age;
};

int main()
{
    Data person;
    char again;
    fstream people;

    people.open("people.db", ios::in | ios::binary);

    if (!people)
    {
        cout << "Error opening file. Program aborting.\n";
        return 0;
    }

    cout << "Here are the people in the file:\n\n";
    people.read(reinterpret_cast<char *>(&person),sizeof(person));

    while (!people.eof())
    {
        cout << "Name: ";
        cout << person.name << endl;
        cout << "Age: ";
        cout << person.age << endl;

        cout << "\nPress the Enter key to see the next record.\n";
        cin.get(again);
        people.read(reinterpret_cast<char *>(&person),sizeof(person));
     }
     cout << "That's all the data in the file!\n";
     people.close();
    return 0;
}

Now when I run read.cpp the program can't read string and the program crashes.现在,当我运行read.cpp时,程序无法读取字符串并且程序崩溃了。 I must use string as a member of the structure.我必须使用字符串作为结构的成员。 How to solve this problem?如何解决这个问题呢?

The only way that comes to mind is to write the following data separately: 想到的唯一方法是分别写入以下数据:

  1. Length of the string. 字符串的长度。
  2. The array of characters of the string. 字符串的字符数组。
  3. The age. 年龄。

and read them separately. 并分别阅读。

Create functions to write/read an instance of Data such that they are aware of each other's implementation strategy. 创建用于写入/读取Data实例的函数,以使它们知道彼此的实现策略。

std::ostream& write(std::ostream& out, Data const& data)
{
   size_t len = data.name.size();
   out.write(reinterpret_cast<char const*>(&len), sizeof(len));
   out.write(data.name.c_str(), len);
   out.write(reinterpret_cast<char const*>(&data.age));
   return out;
}

std::istream& read(std::istream& in, Data& data)
{
   size_t len;
   in.read(reinterpret_cast<char*>(&len), sizeof(len));

   char* name = new char[len+1];
   in.read(name, len);
   name[len] = '\0';
   data.name = name;
   delete [] name;

   in.read(reinterpret_cast<char*>(&data.age));
   return in;
}

and use them similarly to your first approach. 并与您的第一种方法类似地使用它们。

Instead of using 而不是使用

people.write(reinterpret_cast<char *>(&person),sizeof(person));

use 采用

write(people, person);

Instead of using 而不是使用

people.read(reinterpret_cast<char *>(&person),sizeof(person));

use 采用

read(people, person);

One problem is that sizeof(person.Name) does not give what you think it does. 一个问题是sizeof(person.Name)不能提供您认为的功能。 It always gives the same size (28 bytes in my case) not matter what characters you assign to your person.Name string. 无论您分配给person.Name字符串是什么字符,它始终具有相同的大小(在我的情况下为28个字节)。 This is because of std::string contains at least: 这是因为std :: string至少包含:

  • a pointer to the actual string 指向实际字符串的指针
  • other data structure to hold the available size and the size used 其他数据结构以保存可用大小和使用的大小

Therefore, you cannot call people.write(reinterpret_cast<char *>(&person),sizeof(person)); 因此,您无法调用people.write(reinterpret_cast<char *>(&person),sizeof(person)); . The content of your string is not located at &person (its located wherever the pointer in std::string is pointing to) 字符串的内容不位于&person (它位于std :: string中的指针所指向的位置)

So, what happens when you do cout << person.name << endl; 因此,当执行cout << person.name << endl;时会发生什么? after reading it from your file? 从您的文件中读取后? You've actually read the address (not the content) where person.name 's string pointer was pointing to, when you wrote person to people.db. 当您将人写到people.db时,您实际上已经读取了person.name的字符串指针指向的地址(而不是内容)。 This is of course not a valid memory location, after reading it from your file, again. 再次从文件中读取后,这当然不是有效的内存位置。

The following code snippet could be helpful in your case.以下代码片段可能对您的情况有所帮助。 Instead of writing the length of the string, it is possible to use a delimiter and a pre-defined string length.可以使用定界符和预定义的字符串长度来代替写入字符串的长度。

constexpr char delimiter = '\0';
constexpr uint32_t maxStringSize = 1024;

struct Data
{
    string name;
    int age;
};

When writing the file, place a delimiter after the string.写入文件时,在字符串后放置一个delimiter
Let's say we have a Data structure {"John", 42} then we would write as follows:假设我们有一个Data structure {"John", 42}那么我们会这样写:

std::ofstream outStream(filename, std::ios::binary);
outStream << structure.name << delimiter << structure.age;
outStream.close();

Reading the file is not the mirror of the write (unfortunately).读取文件不是写入的镜像(不幸的是)。
We'll use the std::ifstream::getline to read the string without knowing the size of it.我们将使用std::ifstream::getline在不知道字符串大小的情况下读取字符串。 (Error checking omitted) (省略错误检查)

std::ifstream istrm(filename, std::ios::binary);
Data dataRead;

// string input - use a buffer and look for the next delimiter
char* buf = new char[maxStringSize];
istrm.getline(buf, maxStringSize, delimiter);
dataRead.name = std::string(buf);

// the number input
istrm >> dataRead.age;

For inspiration how to read/write a vector of this struct you can check my repository .有关如何读/写此struct的向量的灵感,您可以查看我的存储库

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM