【问题标题】:How do I write binary data to a file in Modern C++?如何在现代 C++ 中将二进制数据写入文件?
【发布时间】:2015-04-29 00:04:21
【问题描述】:

在 C 中将二进制数据写入文件很简单:使用 fwrite,传递要写入的对象的地址和对象的大小。 Modern C++ 是否有更“正确”的东西,还是我应该坚持使用 FILE* 对象?据我所知,IOStream 库用于编写格式化数据而不是二进制数据,并且 write 成员要求 char* 让我在代码中乱扔垃圾。

【问题讨论】:

  • 您使用 std::basic_ostream::writestd::basic_istream::read 函数来写入或读取,例如结构或其他二进制/原始数据。是的,您必须进行类型转换才能使其正常工作。
  • @JoachimPileborg 这应该是一个答案。
  • 这些转换是一个友好的提醒,您正在将数据重新解释为字节序列。如果您不喜欢垃圾,请将 readwrite 包装起来,以便包装器接受 void* 而不是 char*,并在您使用时添加错误处理。
  • 相关问题请参见this mine answer

标签: c++11


【解决方案1】:

所以这里的游戏是在读取和写入时启用依赖于参数的查找,并确保您不要尝试读取/写入非平面数据的内容。

它无法捕获包含指针的数据,也不应该以这种方式读取/写入,但总比没有好

namespace serialize {
  namespace details {
    template<class T>
    bool write( std::streambuf& buf, const T& val ) {
      static_assert( std::is_standard_layout<T>{}, "data is not standard layout" );
      auto bytes = sizeof(T);
      return buf.sputn(reinterpret_cast<const char*>(&val), bytes) == bytes;
    }
    template<class T>
    bool read( std::streambuf& buf, T& val ) {
      static_assert( std::is_standard_layout<T>{}, "data is not standard layout" );
      auto bytes = sizeof(T);
      return buf.sgetn(reinterpret_cast<char*>(&val), bytes) == bytes;
    }
  }
  template<class T>
  bool read( std::streambuf& buf, T& val ) {
    using details::read; // enable ADL
    return read(buf, val);
  }
  template<class T>
  bool write( std::streambuf& buf, T const& val ) {
    using details::write; // enable ADL
    return write(buf, val);
  }
}

namespace baz {
    // plain old data:
    struct foo {int x;};
    // not standard layout:
    struct bar {
      bar():x(3) {}
      operator int()const{return x;}
      void setx(int s){x=s;}
      int y = 1;
    private:
      int x;
    };
    // adl based read/write overloads:
    bool write( std::streambuf& buf, bar const& b ) {
        bool worked = serialize::write( buf, (int)b );
        worked = serialize::write( buf, b.y ) && worked;
        return worked;
    }
    bool read( std::streambuf& buf, bar& b ) {
        int x;
        bool worked = serialize::read( buf, x );
        if (worked) b.setx(x);
        worked = serialize::read( buf, b.y ) && worked;
        return worked;
    }
}

希望你能明白。

live example.

您可能应该限制基于is_pod 而不是标准布局的上述写作,如果在构造/销毁时发生特殊情况,也许您不应该对类型进行二进制位传输。

【讨论】:

  • 看起来不错,但在我接受之前,你能告诉我为什么你声明了一个转发函数吗?
  • @hatcat 简单的 ADL。显式调用serialize::read,它仍然会找到一个本地命名空间read覆盖一个类型。比强迫最终用户在致电read 时始终对 ADL 友好更容易,所以我为他们完成了这项工作。
  • 不错。非常感谢。
  • @hatcat 顺便说一句,有一个不标准的布局 sfinae 测试std::tuple&lt;Ts&amp;...&gt; 重载详细信息。然后聚合可以read(stream,std::tie(my_members...)) 和噗,完成。
【解决方案2】:

由于您已经绕过所有格式,我建议直接使用std::filebuf 类以避免std::fstream 可能产生的开销;由于 RAII,它绝对比 FILE* 更好。

可悲的是,你无法以这种方式逃离演员阵容。但是包装起来并不难,比如:

template<class T>
void write(std::streambuf& buf, const T& val)
{
    std::size_t to_write = sizeof val;
    if (buf.sputn(reinterpret_cast<const char*>(&val), to_write) != to_write)
        // do some error handling here
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-29
    • 2014-10-05
    • 1970-01-01
    • 2014-03-14
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    相关资源
    最近更新 更多