【问题标题】:Weird behavior writing/reading simple binary file写/读简单二进制文件的奇怪行为
【发布时间】:2021-02-14 22:24:34
【问题描述】:

我正在对二进制文件进行读写操作。输出读数时出现小错误。

字符串在那里,但只有很少的 sn-ps,例如:(I"�U) (�U) 附加到其中约 30% 的末尾

我在 Ubuntu 上使用 g++ 编译器

简化代码:

struct Db_connection
{
    public:
        string name;
}

int Db_connection::write_config()
{
    ofstream config_f("config.dat", std::ios_base::binary | std::ios_base::out); //open file

    string str = name;
    int size = str.length();
    
    config_f.write(reinterpret_cast<char *>(&size), sizeof(int)); // write size of string in int size chunk
    config_f.write(str.c_str(), size); //write string

    config_f.close();
    return 0;
}

Db_connection read_config()
{
    ifstream config_f("config.dat", std::ios_base::binary | std::ios_base::in);

    Db_connection return_obj;
    int size;
    string data;

    config_f.read(reinterpret_cast<char *>(&size), sizeof(int)); // read string size
    char buffer[size];
    config_f.read(buffer, size); // read string
    data.assign(buffer);
    return_obj.name = data;

    return return_obj;
}

有什么明显的我搞砸了吗?这和endian有关系吗?我试图将代码最小化为绝对必需品

实际代码更复杂。我有一个包含 2 个结构的向量的类。 1 个结构有四个字符串成员,另一个有一个字符串和布尔值。这些函数实际上是该类的成员并(分别)返回该类。函数循环遍历按顺序写入结构成员的向量。

两个怪事:

  1. 为了调试,我在读取和写入函数的每次迭代中添加了sizedata 变量的输出。 size 两边都准确一致。 datawrite 方面是准确的,但在read 方面具有奇怪的特殊字符。我正在查看如下输出:
Read Size: 12
Data: random addy2�U //the 12 human readable chars are there but with 2 extra symbols
  1. 最后一块数据(一个布尔值)每次都能正常输出,所以我认为不存在文件指针问题。如果相关:每个boolint 都可以。它只是字符串的一部分。

希望我犯了一个愚蠢的错误,并且可以批评这个最小化的代码。实际示例太长了。

【问题讨论】:

  • 首先,停止使用 VLA,即使您的平台上的非标准扩展支持它们。其次,您的写入操作转储字符但没有终止符,您的读取操作读取字符,但没有设置终止符。您正在使用的std::stringassign 方法需要一个终止的字符串。您可以通过多种方式解决此问题,一种简单的方法是 data = std::string(buffer, buffer+size); 。但正如我所说,放弃 VLA 并改用向量。或者更好的是,如果您的工具链标准足够,只需在设置并访问其中的托管缓冲区后直接读入data
  • 感谢您的评论。如果我使用new 命令分配了一个char 数组,那也将是一个VLA?要使用向量,我会将push_back() 逐字节转换为向量?
  • 现代 c++ 中没有任何地方可以使用 operator new。如果您需要大小为size 的临时字符向量,那么std::vector&lt;char&gt; buff(size); 就足够了。然后读入buff.data(),请求的字节数不超过size,最后,data = std::string(buff.begin(), buff.end());不需要手动管理的内存生存期;一切都会自行清理。我仍然会确保您对size 的读取请求实际上是size,但这是另一个问题。
  • 谨防将int 等类型存储在二进制文件中。这不是便携式的。此数据类型的大小是特定于实现的。相反,您应该决定数据大小(例如 16、32、64 位)并使用适当的类型(来自&lt;cstdint&gt; 的内容,例如uint32_t)写入它。需要额外的预防措施来确保字节序兼容性。适当可移植的应用程序将选择正确的数据类型,然后创建一种方法以选择的字节序读取/写入该数据类型。
  • 无论你做什么,将二进制 int 写入输出都会产生垃圾字符。

标签: c++ binaryfiles


【解决方案1】:

非常感谢 WhozCraig,

以下编辑确实有效:


Db_connection read_config()
{
    ifstream config_f("config.dat", std::ios_base::binary | std::ios_base::in);

    Db_connection return_obj;
    int size;
    string data;

    config_f.read(reinterpret_cast<char *>(&size), sizeof(int)); // read string size
    vector<char> buff(size);
    config_f.read(buff.data(), size);
    data = string(buff.begin(), buff.end());
    return_obj.name = data;

    return return_obj;
}

正如 paddy 直接指出和 WhozCraig 暗示的那样,这段代码仍然需要实现一个标准化的、可移植的数据类型,以便将整数正确地记录为二进制,并且还需要重新考虑写入函数。

非常感谢你们俩。在编写我的代码之前,我阅读了 5-8 个“cpp 二进制 i/o”的热门搜索结果,但最终还是一团糟。你们救了我几个小时/几天的生命。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-14
    • 1970-01-01
    • 1970-01-01
    • 2012-01-26
    • 2018-07-26
    • 2016-06-14
    • 2021-06-03
    相关资源
    最近更新 更多