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

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

本文关键字:读取 结构 成员 类型 字符串 输出 c++ 输入 二进制文件      更新时间:2023-10-16

>我有 2 个 c++ 代码:一个用于将数据写入二进制文件,另一个用于读取该文件。

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代码如下:

#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:nn";
    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;
}

上面提到的代码工作正常。当我在结构中使用字符串类型成员时出现问题:

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;
}

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:nn";
    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;
}

现在,当我运行read.cpp程序无法读取字符串并且程序崩溃时。我必须使用字符串作为结构的成员。如何解决这个问题?

想到的唯一方法是单独写入以下数据:

  1. 字符串的长度。
  2. 字符串的字符数组。
  3. 年龄。

并单独阅读它们。

创建函数来写入/读取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] = '';
   data.name = name;
   delete [] name;
   in.read(reinterpret_cast<char*>(&data.age));
   return in;
}

并像使用第一种方法一样使用它们。

而不是使用

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

write(people, person);

而不是使用

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

read(people, person);

一个问题是sizeof(person.Name)没有给出你认为它所做的事情。它总是提供相同的大小(在我的例子中为 28 字节),无论您分配给您的人什么字符。名称字符串。这是因为 std::string 至少包含:

  • 指向实际字符串的指针
  • 其他数据结构,以保存可用大小和所用大小

因此,您不能调用people.write(reinterpret_cast<char *>(&person),sizeof(person)); 。字符串的内容不位于 &person(它位于 std::string 中的指针指向的任何位置)

那么,当您从文件中读取cout << person.name << endl;后执行时会发生什么?你实际上已经阅读了person.name的字符串指针指向的地址(而不是内容),当你写人给人.db时。这当然不是一个有效的内存位置,在从您的文件中读取它之后,再次。

以下代码片段可能对您的情况有所帮助。可以使用分隔符和预定义的字符串长度,而不是写入字符串的长度。

constexpr char delimiter = '';
constexpr uint32_t maxStringSize = 1024;
struct Data
{
    string name;
    int age;
};

写入文件时,在字符串后放置一个delimiter.
假设我们有一个Data structure {"John", 42}那么我们将写如下:

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

读取文件不是写入的镜像(不幸的是).
我们将使用 std::ifstream::getline 来读取字符串,而无需知道它的大小。(错误检查省略)

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;

对于如何读取/写入此struct向量的灵感,您可以查看我的存储库。