简体   繁体   English

如何仅使用标准库将 C++ 对象写入文件

[英]How to write C++ objects to files using only the standard library

I'll start by clarifying what makes this question not a dupe:我将首先澄清是什么让这个问题不是骗局:

  • Anything that is outside the standard C++ library is is banned, that includes boost and the other libraries禁止标准 C++ 库之外的任何内容,包括 boost 和其他库
  • The objects may contain malloc -ed C-style arrays对象可能包含malloc -ed C 样式 arrays

Before flagging this question as a dupe, please provide a question where these 2 requirements are addressed.在将此问题标记为欺骗之前,请提供一个满足这两个要求的问题。

Before flagging this question as not useful, please consider that writing objects with bad data members may be a legitimate requirement for my upcoming C++ test in college.在将此问题标记为无用之前,请考虑编写具有错误数据成员的对象可能是我即将在大学进行的 C++ 测试的合法要求。 I've seen some tests for the previous years, where it was stated that a class BankAccount for example must have 2 dynamically allocated double * .我看过前几年的一些测试,其中指出例如 class BankAccount必须有 2 个动态分配的double * As to why only use the standard library, I'm afraid it's quite likely our teacher doesn't know how to use boost, or anything except std.至于为什么只使用标准库,恐怕我们老师很可能不知道如何使用boost,或者除了std之外的任何东西。

I have tried this code, which unfortunately doesn't work quite right:我已经尝试过这段代码,不幸的是它不能很好地工作:

#include<fstream>
#include<iostream>

using namespace std;

class Student
{
    int roll;
    char name[25];
    float marks;
    int *arr;
    double *second;
public:
    friend ostream& operator<<(ostream& out, const Student &s)
    {
        out << s.marks << s.name << s.roll;
        for (int i = 0; i < 5; ++i) {
            out << s.arr[i];
        }
        for (int i = 0; i < 10; ++i) {
            out << s.second[i];
        }
        out << std::endl;
        return out;
    }
    friend istream& operator>>(istream& ins, Student &s)
    {
        ins >> s.marks >> s.name >> s.roll;
        for (int i = 0; i < 5; ++i) {
            ins >> s.arr[i];
        }
        for (int i = 0; i < 10; ++i) {
            ins >> s.second[i];
        }
        return ins;
    }

    Student() : roll(10), name("Platon"), marks(4)
    {
        arr = new int[5];
        second = new double[10];
        for (int i = 0; i < 5; ++i) {
            arr[i] = i + 1;
        }
        for (int j = 0; j < 10; ++j) {
            second[j] = j + 1;
        }
    }
    Student(int r, char n[25], float m, int* a, double* s): roll(r), marks(m)
    {
        arr = new int[5], second = new double[10];
        for (int i = 0; i < 25; ++i) {
            name[i] = n[i];
        }
        for (int j = 0; j < 5; ++j) {
            arr[j] = a[j];
        }
        for (int k = 0; k < 10; ++k) {
            second[k] = s[k];
        }
    }
};

int main()
{
    int dummy[] = {1, 2, 3, 4, 5};
    double another[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    ofstream fp("file.bin", ofstream::out|ofstream::binary);
    Student S(56, "Platon Makovsky", 14.2, dummy, another);
    cout << "writing: "; cout << S << std::endl;
    fp << S;
    fp.close();
    Student test;
    ifstream f("file.bin", ifstream::in|ifstream::binary);
    f >> test;
    cout << "read: "; cout << test << std::endl;
    f.close();

    return 0;
}

This program generates this output:这个程序生成这个 output:

writing: 14.2Platon Makovsky561234512345678910

read: 14.2Platon01234512345678910

For some reason the program failed to read the complete name and the roll.由于某种原因,程序未能读取完整的名称和卷。 Any help with this problem is appreciated.对此问题的任何帮助表示赞赏。

Btw I know about using namespace std;顺便说一句,我知道using namespace std; , I just didn't feel like writing std:: all the time. ,我只是不想一直写std:: The destructor has been omitted for the sake of brevity, I know about memory management too.为简洁起见省略了析构函数,我也知道 memory 管理。

Okay, there are reasons why there are formal definitions of file formats like XML and JSON.好的,有一些文件格式的正式定义,如 XML 和 JSON 是有原因的。 You're hitting it.你在打它。

You need some means of determining where individual fields start and end.您需要一些方法来确定各个字段的开始和结束位置。 Imagine, for instance, that you decide to put each field on its own line.例如,想象一下,您决定将每个字段放在自己的一行上。 This is perfectly legitimate.这是完全合法的。 Write them out, one per line, and when you read them back, read lines from the file and convert to the proper data type.将它们写出来,每行一个,当你读回它们时,从文件中读取行并转换为正确的数据类型。 Easy.简单的。

Until you put out a string with a newline in it.直到你输出一个带有换行符的字符串。 Now that value is spanning two lines, which you probably didn't consider.现在该值跨越两行,您可能没有考虑过。

So I would write a function that converts strings to "safe strings".所以我会写一个 function 将字符串转换为“安全字符串”。 Specifically, convert every instance of newline to literally the characters \n.具体来说,将每个换行符实例转换为字符 \n。 While at it, convert a single \ to two \.在此期间,将单个 \ 转换为两个 \。 Imagine this string:想象一下这个字符串:

This is foo\bar.
And it had a newline.

This would get turned into:这将变成:

This is foo\\bar.\nAnd it had a newline.

(Literally -- that's not a newline, that's a string representation you can recognize as a newline.) (从字面上看——这不是换行符,而是您可以识别为换行符的字符串表示形式。)

Then write the reverse function.然后反写function。 It converts \ into a single \ and \n into a proper newline again.它将 \ 转换为单个 \ 并再次将 \n 转换为正确的换行符。

That's step one.这是第一步。

Step two: decide how you're going to indicate a field is null.第二步:决定如何指示一个字段是 null。 You could just write null.你可以只写 null。 That will work for numeric fields, but if you do that, what are you going to do about char * that's null?这将适用于数字字段,但如果你这样做,你将如何处理 char * 即 null? You can still write null, but what if your value is literally the text null?你仍然可以写 null,但是如果你的值是字面上的文本 null 怎么办? Decide how you want to represent this.决定你想如何表示这一点。 You could have pointers be something like "value=12345" or "value=abcde", and then null could just be "null".您可以让指针类似于“value=12345”或“value=abcde”,然后 null 可能只是“null”。 That would be safe.那将是安全的。

Step three: write your fields, one per line.第三步:写下你的字段,每行一个。 Use this safe string method when writing strings, and do whatever you're going to do about pointers.在编写字符串时使用这种安全的字符串方法,并且对指针做任何你想做的事情。

Step four: write a method that reads the file one line at a time and just print it out (so you know it works).第四步:编写一个方法,一次读取一行文件,然后打印出来(这样你就知道它有效)。

Step five: use this to produce your new object (reading from the file).第五步:使用它来生成新的 object(从文件中读取)。

if you do this correctly, you should be able to handle all the basic data types.如果您正确执行此操作,您应该能够处理所有基本数据类型。

It might take you an hour or two.这可能需要你一两个小时。

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

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