代码之家  ›  专栏  ›  技术社区  ›  hatcat

如何用现代C++将二进制数据写入文件?

  •  3
  • hatcat  · 技术社区  · 11 年前

    用C语言将二进制数据写入文件很简单:使用fwrite,传递要写入的对象的地址和对象的大小。现代C++有什么更“正确”的东西吗?还是我应该坚持使用FILE*对象?据我所知,IOStream库用于编写格式化数据,而不是二进制数据,编写成员要求一个char*,让我在代码中乱丢强制转换。

    2 回复  |  直到 11 年前
        1
  •  4
  •   Yakk - Adam Nevraumont    11 年前

    因此,这里的游戏是在读写时启用依赖于参数的查找,并确保不尝试读写非平面数据的内容。

    它无法捕获包含指针的数据,也不应该这样读/写,但总比什么都没有好

    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 这不是标准的布局,如果在构造/销毁时发生了特殊情况,也许您不应该对类型进行二进制blitting。

        2
  •  2
  •   DanielKO    11 年前

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

    遗憾的是,你不能以这种方式逃离演员阵容。但包装起来并不难,比如:

    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
    }