【问题标题】:Unexpected results with std::ofstream binary writestd::ofstream 二进制写入的意外结果
【发布时间】:2014-10-14 04:38:43
【问题描述】:

我是 C++ std::stream 的新手,我正在做一些测试。我有这个简单的代码:

int i = 10;
char c = 'c';
float f = 30.40f;

std::ofstream out("test.txt", std::ios::binary | std::ios::out);
if(out.is_open())
{
    out<<i<<c<<f;
    out.close();
}

由于流以std::ios::binary 打开,我希望在test.txt 文件中具有icf 的二进制表示,但我有10c30.4

你能告诉我我做错了什么吗?

【问题讨论】:

  • 来自cplusplus.com/reference/fstream/ofstream/ofstream binary (binary) 将流视为二进制而不是文本。
  • 好的,然后是文本文件流与二进制文件流的 POSIX 定义 :) (提示:为什么将 text 写入二进制文件会导致其他结果文字?)
  • @Felics:该标准没有指定“二进制”的实际含义。事实上,在某些平台上它没有效果(例如在 POSIX 上)。
  • 非常清楚:+1 这个问题。我认为有趣的是发现documentation on this 非常粗略。这显然是“假设”是显而易见的。更多的人可能会感到困惑,这使得这是一个完美的Stack Overflow 候选人。
  • @Felics:你的最后一句话毫无意义。 ISO 表示使用binary|out 打开fstream 与使用"wb" 模式的fopen等效。现在,当你说“我有预期的结果”时,它意味着你调用了fwrite,它对应于ostream::write。但是您的代码在这里使用operator &lt;&lt;,它对应于例如fprintf(out, "%d", i) 表示整数。

标签: c++ iostream


【解决方案1】:

std::ios::binary 承诺不对流进行任何行尾转换(以及与文本流的一些其他小的行为差异)。

你可以看看

这是一个使用 Boost Spirit Karma 的示例(假设 Big-Endian 字节排序):

#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;

int main()
{
    int i = 10;
    char c = 'c';
    float f = 30.40f;

    std::ostringstream oss(std::ios::binary);
    oss << karma::format(
            karma::big_dword << karma::big_word << karma::big_bin_float, 
            i, c, f);

    for (auto ch : oss.str())
        std::cout << std::hex << "0x" << (int) (unsigned char) ch << " ";
    std::cout << "\n";
}

打印出来

0x0 0x0 0x0 0xa 0x0 0x63 0x41 0xf3 0x33 0x33 

【讨论】:

  • 我添加了一个基于 Karma 的演示,另请参见此处:http://liveworkspace.org/code/4B5okj
  • 这不是“故事的结尾”,可以对文本文件执行各种可能的转换,包括读回的数据可能与写入的数据不同(例如修剪尾随空格),以及某些文件操作(例如 fseek)对于每种类型的行为都不同。
【解决方案2】:

要写入原始二进制数据,您必须使用ostream::write。它不适用于输出运算符。

还要确保如果要从二进制文件中读取,不要使用运算符>>,而是使用istream::read

这些链接还提供了如何处理二进制数据的示例。

所以对于你的例子:

int i = 10;
char c = 'c';
float f = 30.40f;

std::ofstream out("test.txt", std::ios::binary | std::ios::out);
if(out.is_open())
{
    out.write(reinterpret_cast<const char*>(&i), sizeof(i));
    out.write(&c, sizeof(c));
    out.write(reinterpret_cast<const char*>(&f), sizeof(f));
    out.close();
}

【讨论】:

  • 在执行此操作时要注意 UB 和不可移植性。我建议使用经过良好测试的库来为您处理这个问题,并考虑字节序(Boost Spirit、序列化、protobuf 等)
  • 是的,你是对的。但我只是想用没有任何 boost 或其他库的普通 C++ 指出一个答案。
  • 例如我认为,该示例在技术上要求 reinterpret_cast&lt;const char*&gt; 不调用未定义行为。 (PS。我确实也用纯C++指出了答案:))
  • @sehe 实际上,标准保证(= 不调用 UB 的代码)。这样做的结果,现在未指定那个
  • 我认为您缺少一个 &amp; 或三个。还有几个sizeofs。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
相关资源
最近更新 更多