使用fstream加载二进制文件

时间:2009-07-20 18:01:16

标签: c++ stl file-io filestream fstream

我正在尝试使用fstream以下列方式加载二进制文件:

#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>

using namespace std;

int main()
{
    basic_fstream<uint32_t> file( "somefile.dat", ios::in|ios::binary );

    vector<uint32_t> buffer;
    buffer.assign( istream_iterator<uint32_t, uint32_t>( file ), istream_iterator<uint32_t, uint32_t>() );

    cout << buffer.size() << endl;

    return 0;
}

但它不起作用。在Ubuntu中,它因std::bad_cast异常而崩溃。在MSVC ++ 2008中,它只打印0。

我知道我可以使用file.read来加载文件,但我想使用iterator和operator>>来加载文件的一部分。那可能吗? 为什么上面的代码不起作用?

4 个答案:

答案 0 :(得分:3)

  1. istream_iterator希望basic_istream作为参数。
  2. 无法在operator>>类内重载basic_istream
  3. 定义全局operator>>将导致与类成员operator>>的编译时冲突。
  4. 您可以将basic_istream专门用于uint32_t类型。但是对于专业化,你应该重写basic_istream类的所有功能。相反,您可以为其定义虚拟类x并为其专门化basic_istream,如下面的代码所示:
  5. using namespace std;
    
    struct x {};
    namespace std {
    template<class traits>
    class basic_istream<x, traits> : public basic_ifstream<uint32_t>
    {
    public:
        explicit basic_istream<x, traits>(const wchar_t* _Filename, 
            ios_base::openmode _Mode, 
            int _Prot = (int)ios_base::_Openprot) : basic_ifstream<uint32_t>( _Filename, _Mode, _Prot ) {}
    
        basic_istream<x, traits>& operator>>(uint32_t& data)
        {
            read(&data, 1);
            return *this;
        }
    };
    } // namespace std 
    
    int main() 
    {
        basic_istream<x> file( "somefile.dat", ios::in|ios::binary );
        vector<uint32_t> buffer;
        buffer.assign( istream_iterator<uint32_t, x>( file ), istream_iterator<uint32_t, x>() );
        cout << buffer.size() << endl;
        return 0;
    }
    

答案 1 :(得分:0)

主要问题可能是“二进制文件”的含义。 ios::binary仅确保istream对象不会通过'\ n'替换特定于平台的换行符。没有其他的。这对你来说够了吗?

istream_iterator基本上只是一种调用operator>>的奇特方式。如果您的流中有真正的二进制数据,则会失败。你的文件中有真正的二进制数据吗?或整数存储为字符串?

如果需要读取实数二进制整数,您需要的是istream.read()或直接使用流缓冲区对象。

答案 2 :(得分:0)

您可以重新加载运算符&gt;&gt;正确读取整数。当然它所做的只是read()4个字节。但这就是所有其他运营商&gt;&gt;无论如何最终都在做。

这是示例(没有错误检查,假设endianess与当前编译器使用的相同,等等)

std::istream& operator>>(std::istream& in, uint32_t& data)
{
    in.read(&data, sizeof(data));
    return in;
}

为您自己的整数风格量身定制(可能必须一次读取一个字节并转移分配它们,如果您不知道字节顺序,请在十六进制编辑器中查看文件),添加错误检查,您应该是能够使用您现有的代码。

编辑:嗯,是的,确保这个阴影提供了读取整数的stl操作符 - 可能必须从您正在使用的流中派生自己的类,并使用它而不是std :: istream&amp; in,只是让编译器知道先检查谁。

答案 3 :(得分:0)

与Alexey Malistov的回答一样的另一种方式:

#include <fstream>
#include <iterator>
#include <vector>
#include <iostream>

struct rint // this class will allow us to read binary
{
  // ctors & assignment op allows implicit construction from uint
  rint () {}
  rint (unsigned int v) : val(v) {}
  rint (rint const& r) : val(r.val) {}
  rint& operator= (rint const& r) { this->val = r.val; return *this; }
  rint& operator= (unsigned int r) { this->val = r; return *this; }

  unsigned int val;

  // implicit conversion to uint from rint
  operator unsigned int& ()
  {
    return this->val;
  }
  operator unsigned int const& () const
  {
    return this->val;
  }
};

// reads a uints worth of chars into an rint
std::istream& operator>> (std::istream& is, rint& li)
{
  is.read(reinterpret_cast<char*>(&li.val), 4);
  return is;
}

// writes a uints worth of chars out of an rint
std::ostream& operator<< (std::ostream& os, rint const& li)
{
  os.write(reinterpret_cast<const char*>(&li.val), 4);
  return os;
}

int main (int argc, char *argv[])
{
  std::vector<int> V;

  // make sure the file is opened binary & the istream-iterator is
  // instantiated with rint; then use the usual copy semantics
  std::ifstream file(argv[1], std::ios::binary | std::ios::in);
  std::istream_iterator<rint> iter(file), end;
  std::copy(iter, end, std::back_inserter(V));

  for (int i = 0; i < V.size(); ++i)
    std::cout << std::hex << "0x" << V[i] << std::endl;

  // this will reverse the binary file at the uint level (on x86 with
  // g++ this is 32-bits at a time)
  std::ofstream of(argv[2], std::ios::binary | std::ios::out);
  std::ostream_iterator<rint> oter(of);
  std::copy(V.rbegin(), V.rend(), oter);

  return 0;
}