c++ - 文件 I/O 二进制动态数组崩溃

标签 c++ file oop file-writing file-read

#include iostream
#include cmath
#include fstream
#include cstdlib
#include string
using namespace std;
class Device {//Input and store Device Description and Serial Numbers
protected:
    string  serial_number;
    string device_description;
public:
    Device() {
        serial_number = ("6DCMQ32");
        device_description = ("TheDell");
    }
};
class Test {//Input and store Test Description, recent day, and month; 
Calculate the next day
protected:
    string Test_Description;
    static int recent_month, recent_day, recent_year, new_month;
    static int nmonth, next_month, next_day, next_year, max_day;
public:
    Test() {
        Test_Description = ("Virtual");
    }
    static void getMonth(ostream & out) {//Calculates the next/new month
        next_month = recent_month + nmonth;
        new_month = next_month % 12;
        if (next_month >= 12) {
            cout << "The next Date: " << new_month << " / ";
        }
        else {
            out << "The next Date: " << next_month << " / ";
        }
    }
    static void getDay(ostream & out) { //Calculates day of next month
        if (new_month == 4 || new_month == 6 || new_month == 9 || new_month == 11) {
            max_day = 30;
        }
        else if (new_month == 2) {
            max_day = 29;
        }
        else {
            max_day = 31;
        }
        if (recent_day > max_day) {
            out << max_day << " / ";
        }
        else {
            out << recent_day << " / ";
        }
    }
    static void getYear(ostream & out) {// Calculate the year of next month
        next_year = recent_year + next_month;
        if (next_year >= 12) {
            out << recent_year + (next_month / 12) << endl;
        }
        else {
           out << next_year << endl;
        }
    }
    static void getDate(ostream & out) {// Collects the output of each element of next date
        getMonth(out), getDay(out), getYear(out);
    }
};
int Test::recent_month;
int Test::recent_day;
int Test::recent_year;
int Test::new_month;
int Test::nmonth;
int Test::next_month;
int Test::next_day;
int Test::next_year;
int Test::max_day;
class Lab : public Device, public Test {
protected:
    static int n;
public:
    friend istream & operator>>(istream & in, Lab & lab) {// Inputs 
        cout << "Enter Device Desciption and Serial Number: ";
        getline(cin, lab.device_description);
        getline(cin, lab.serial_number);
        cout << "Enter Test Desciption: ";
        getline(cin, lab.Test_Description);
        cout << "Enter the Number of months: ";
        in >> nmonth;
        cout << "Enter the Most Recent Date(mm/dd/yyyy): ";
        in >> recent_month >> recent_day >> recent_year;
        return in;
    }
    friend ostream & operator<<(ostream & out, Lab & lab) {//Outputs everything in Device Class
        out << lab.device_description << endl;
        out << lab.serial_number << endl;
        out << lab.Test_Description << endl;
        getDate(out);
        return out;
    }
    static void getN() {
        cout << "Enter the number of devices: ";
        cin >> n;
    }
    static void getWrite() {
        Lab *obj = new Lab[n];
        if (obj == 0) {
            cout << "Memory Error";
            exit(1);
        }
        for (int i = 0; i<n; i++) {
            cin >> obj[i];
            cout << endl;
        }
        ofstream myfile("Device.dat", ios::binary);
        myfile.write((char*) obj, n * sizeof(Lab));
        delete[] obj;
    }
    static void getRead() {
        ifstream file2("Device.dat", ios::binary);
        Lab *obj2 = new Lab[n];
        if (obj2 == 0) {
            cout << "Memory Error";
            exit(1);
        }
        file2.read((char*) obj2, n * sizeof(Lab));
        for (int i = 0; i < n; i++) {
            cout << obj2[i];
            cout << endl;
        }
        delete[] obj2;
    }
};
int Lab::n;
void main() {
    Lab L;
    L.getN();
    L.getWrite();
    L.getRead();
    getchar();
    getchar();
    system("pause");
}

输出值后程序一直崩溃

目的:输入序列号输入设备下一次测试日期的月数, 设备描述、测试描述、最近日期和两次测试的月数。最后 必须通过让用户输入序列号和下一个日期来搜索程序,如果这两个是 列出设备中的所有内容。

我正在使用 Microsoft Visual Studios 2017

最佳答案

std::string不幸的是,数据结构过于复杂,无法简单地写入文件。最简单的,一个 string是指向字符数组的指针和存储数组长度的整数。当你写一个指向文件的指针时,你写的是地址,而不是地址上的数据。当您阅读 string回到后者,很有可能你会得到一个指向程序不拥有的内存的陈旧地址和很多崩溃的坏处。更糟糕的是,如果回读地址指向程序中确实存在的内容。这些通常不会立即崩溃并使您远离实际的错误,因为破坏者正坐在另一段代码中,完全得意洋洋地吹口哨,因为您责备和调试错误的代码。无论哪种方式访问​​尚未分配给指针的内存都会调用 Undefined Behaviour ,而有了 UB,一切皆有可能。不要指望崩溃或一致性。

通常您会使用 operator <<重载到 serialize the structure到文件而不是尝试二进制写入。如果您必须执行二进制写入,则需要创建一个协议(protocol)来描述必须如何写入和读取数据。

该协议(protocol)将是一组函数,可将更简单的数据类型与其等效文件进行相互转换。

编写 string 的典型方法就是先写string的长度然后写入string的内容.有点像

uint32 len = str.length(); //fixed width length
len = htonl(len); // fixed endian
myfile.write((char*) &len, sizeof(len)); //write length
myfile.write(str.data(), str.length()); //write string

阅读

uint32 len; //fixed width length
myfile.read((char*) &len, sizeof(len)); //read length
len = ntohl(len); // unfix endian
std::string str(len, ' '); //string and allocate size
myfile.write(str.data(), len); //read string C++17 or
//myfile.write(&str[0], len); //read string before C++17

将它们捆绑在函数中,您就可以开始使用您的协议(protocol)了。向它们添加您需要存储的其他数据类型的函数。

然后,这些函数会被转换更大数据类型的函数调用,直到您达到需要编写的最复杂的结构。对于数组,使用循环。如果你有一个可变大小的长度,就像使用 string 一样为长度添加前缀。 .

旁注:读取或写入数字时,必须小心确保数字是已知的固定大小。 int ,例如 , can be any size 16 bits or greater so long at it's not larger than. You don't necessarily know that the file reader will be using the same sized int`,所以你应该更喜欢 Fixed Width Integer足够大以存储所需的值。不同的计算机也可能以不同的顺序存储它们的二进制信息。这叫做 Byte Order or Endian .确保每个人都使用相同的字节序。

关于c++ - 文件 I/O 二进制动态数组崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53384784/

相关文章:

c++ - 做复合 C++ 时成员函数重载错误

c++ - Live555 RTSP服务器不使用UDP

c++ - 为什么 ostream::write() 在 C++ 中需要 ‘const char_type*’ 而不是 ‘const void*’?

c++ - 从 vector c++ 中获取匹配项的索引

Java - 将 ArrayList 打印到文本文件中

python - 在类中的函数之间传递变量

c++ - 断言中的 dynamic_cast 导致错误

java - 将数组的字符串输出到 java 中的 JTextArea

c++ - 如何从 ASCII 文本文件中读取(小)整数到 C++ 中足够的数据数组

c# - 针对接口(interface)编程而不是实现混淆