C++ 二进制读取到动态字符

C++ binary reading to dynamic char

我的代码有问题,我不知道到底是什么导致了这个问题。

Source.cpp

#include "class.h"

int main()
{
    Book book1("At Mountain of Madness", "H.P Lovecraft", 1936);
    book1.binaryFileWrite();
    book1.binaryFileRead();

    Book book2("Danwych's horror", "H.P Lovecraft", 1929);
    book2.binaryFileWrite();
    book2.binaryFileRead();

    
}
#ifndef BOOK_BINARY_LIBRARY
#define BOOK_BINARY_LIBRARY

#include <cstring>
#include <iostream>
#include <fstream>
#include <string>

class Book
{
public:
    Book()
        : _year(0)
    {
        _title = new char[10];
        _author = new char[10];
        strcpy(_title, "Undefined");
        strcpy(_author, "Undefined");
    }

    Book(const char *title, const char *author, int year)
        : _year(year)
    {
        _titleLen = strlen(title);
        _authorLen = strlen(author);
        _title = new char[_titleLen + 1];
        _author = new char[_authorLen + 1];
        strcpy(_title, title);
        strcpy(_author, author);
    }

    Book(const Book& book)
    {
        _titleLen = strlen(book._title);
        _authorLen = strlen(book._author);
        _title = new char[_titleLen + 1];
        _author = new char[_authorLen + 1];
        strcpy(_title, book._title);
        strcpy(_author, book._author);
    }

    ~Book()
    {
        delete[] _title;
        delete[] _author;
    }
    void display() 
    {
        std::cout << _title << " " << _author << " " << _year << std::endl;
    }

    void binaryFileWrite(const std::string &fileName = "binaryLibrary.bin")
    {
        std::ofstream file(fileName, std::ios::binary);
        if (!file.is_open()) {
            std::cout << "Failed" << std::endl;
            exit(1);
        }
        else {
            /*file.write(_title, sizeof(_title));
            file.write(_author, sizeof(_author));
            file.write(reinterpret_cast<char*>(&_year), sizeof(int));*/
            file.write(reinterpret_cast<char*>(this), sizeof(Book));
        }
        file.close();
        
    }

    void binaryFileRead(const std::string& fileName = "binaryLibrary.bin")
    {
        std::ifstream file(fileName, std::ios::binary);
        if (!file.is_open()) {
            std::cout << "Failed" << std::endl;
            exit(2);
        }
        else {
            Book read;
            file.read(reinterpret_cast<char*>(&read), sizeof(Book));
            std::cout << read._title << " " << read._author << " " << read._year <<  std::endl;
        }
        file.close();
    }


private:
    char* _title;
    char* _author;
    int _year{ 0 };
    int _titleLen{ 0 };
    int _authorLen{ 0 };
};

#endif

问题肯定出在这部分代码:

void binaryFileRead(const std::string& fileName = "binaryLibrary.bin")
    {
        std::ifstream file(fileName, std::ios::binary);
        if (!file.is_open()) {
            std::cout << "Failed" << std::endl;
            exit(2);
        }
        else {
            Book read;
            file.read(reinterpret_cast<char*>(&read), sizeof(Book));
            std::cout << read._title << " " << read._author << " " << read._year <<  std::endl;
        }
        file.close();
    }

它甚至打印了我想要控制台应用程序的内容,但仍然没有 return 0.

我不知道是什么问题,我试了很多都没有帮助,我什至认为它无法从 .bin 文件读取到动态字符或其他东西。

顺便说一句,我的第一个原型没有使用动态数组,我使用了固定大小的数组,如 _title[50]_author[50],并且工作正常。我会说完美,但是当我尝试使用动态数组时它失败了。

file.write(reinterpret_cast<char*>(this), sizeof(Book));

sizeof(Book) 计算 Book 对象的大小。它是一个编译时常量值。这意味着您将始终获得 sizeof(Book) 的相同结果。您的 Book 对象包含两个 char 指针和三个 int。在 64 位平台上,此 Book 对象的长度应为 40 个字节,而 sizeof(Book) 将为 40 个字节,并且上述 write 语句将始终尝试将恰好 40 个字符写入文件。

总是。

这两个char *是指向空字符串,还是指向哈利波特小说的全部内容都没有关系。您将始终将 40 个字节写入文件,其中将包括这两个 char * 碰巧拥有的一对原始内存地址。当然,将原始指针值写入文件是完全没有意义的,不会产生任何有用的结果。

读取文件时也是如此。

您将需要以其他方式重新实现读取或写入文件的整个逻辑。也许首先写入所有整数值,然后是每个字符串的长度(以字节为单位),然后是每个字符串。当你读回它时,你需要实现相反的逻辑。你不需要完全那样做,你可以自由设计任何适合你的 serialization/deserialization 格式(正如它所说的),但你不能 readwrite 一个带有指针或非平凡成员的对象,收工吧。不幸的是,C++ 并没有那么简单。

主要问题是 Book 包含 指向存储在内存中其他地方的数据的指针 。当您 read/write 按原样 Book 对象时,您 reading/writing 那些指针原样,而不是它们指向的数据。因此,您需要单独处理 char* 数据。

此外,您的 binaryFileRead() 甚至没有读入调用它的 Book 对象,它正在读入一个单独的临时 Book 对象。

试试这个:

void binaryFileWrite(const std::string &fileName = "binaryLibrary.bin")
{
    std::ofstream file(fileName, std::ios::binary);
    if (!file.is_open()) {
        std::cout << "Failed" << std::endl;
        exit(1); // TODO: throw an exception instead...
    }
    binaryWrite(file);
}

void binaryWrite(std::ostream &out)
{
    out.write(reinterpret_cast<char*>(&_titleLen), sizeof(_titleLen));
    out.write(_title, _titleLen);

    out.write(reinterpret_cast<char*>(&_authorLen), sizeof(_authorLen));
    out.write(_author, _authorLen);

    out.write(reinterpret_cast<char*>(&_year), sizeof(_year));
}

void binaryFileRead(const std::string& fileName = "binaryLibrary.bin")
{
    std::ifstream file(fileName, std::ios::binary);
    if (!file.is_open()) {
        std::cout << "Failed" << std::endl;
        exit(2); // TODO: throw an exception instead...
    }
    binaryRead(file);
}

void binaryRead(std::istream &in)
{
    int len;

    in.read(reinterpret_cast<char*>(&len), sizeof(_len));
    char *s = new char[len + 1];
    in.read(s, len);
    delete[] _title; _title = s;
   _titleLen = len;

    in.read(reinterpret_cast<char*>(&len), sizeof(_len));
    s = new char[len + 1];
    in.read(s, len);
    delete[] _author; _author = s;
   _authorLen = len;

    in.read(reinterpret_cast<char*>(&_year), sizeof(_year));
}

附带说明一下,您的代码还有其他问题:

  • 您的默认构造函数未初始化 _titleLen_authorLen 字段。您真的应该考虑对 _title_author 字段使用 std::string,而不是使用 char*+int。特别是因为您在代码的其他地方使用 std::string

  • 您的复制构造函数没有复制 _year 字段。

  • 您的 Book class 不符合 Rule of 3,因为它缺少复制赋值运算符。

  • 如果将 _title_author 字段切换为 std::string,则不需要实现复制构造函数、复制赋值运算符或析构函数,因为默认的编译器生成的就足够了。