使用 fstream 加载二进制文件

发布于 2024-07-27 00:58:46 字数 698 浏览 7 评论 0原文

我尝试按以下方式使用 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 来加载文件,但我想使用迭代器和运算符来加载部分文件文件。 那可能吗? 为什么上面的代码不起作用?

I'm trying to load binary file using fstream in the following way:

#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;
}

But it doesn't work. In Ubuntu it crashed with std::bad_cast exception. In MSVC++ 2008 it just prints 0.

I know that I could use file.read to load file, but I want to use iterator and operator>> to load parts of the file. Is that possible? Why the code above doesn't work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

天赋异禀 2024-08-03 00:58:46
  1. istream_iterator 想要 basic_istream 作为参数。
  2. 不可能在 basic_istream 类中重载 operator>>
  3. 定义全局operator>>将导致与类成员operator>>发生编译时冲突。
  4. 您可以将 basic_istream 专门化为 uint32_t 类型。 但为了专业化,您应该重写 basic_istream 类的所有功能。 相反,您可以定义虚拟类 x 并为其专门化 basic_istream ,如以下代码所示:
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. istream_iterator wants basic_istream as argument.
  2. It is impossible to overload operator>> inside basic_istream class.
  3. Defining global operator>> will lead to compile time conflicts with class member operator>>.
  4. You could specialize basic_istream for type uint32_t. But for specialization you should rewrite all fuctionons of basic_istream class. Instead you could define dummy class x and specialize basic_istream for it as in the following code:
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;
}
一人独醉 2024-08-03 00:58:46

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

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

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

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

编辑:啊,是的,确保这个阴影提供了读取整数的 stl 运算符——可能必须从您正在使用的流派生您自己的类,并使用它而不是 std::istream& in,这样编译器就知道首先检查谁。

You can reload operator>> to read integers properly. Of course all it will do is read() 4 bytes. But that's what all other operators>> are eventually doing anyway.

Here is example (no error checking, assuming endianess is same as current compiler uses, etc)

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

Tailor for your own flavor of integers (might have to read one byte at a time and shift assign them, look at the file in hex editor if you don't know byte order), add error checking, and you should be able to use your existing code.

EDIT: ah, and yes, make sure this shadows provided stl operator that reads integer -- might have to derive your own class from the stream you are using and use that instead of std::istream& in, just so compiler knows who to check first.

_蜘蛛 2024-08-03 00:58:46

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

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

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

The main question probably is what you mean by "binary file". The ios::binary only makes sure that the istream object doesn't replace platform-specific newlines by '\n'. Nothing else. Is that enough for you?

An istream_iterator basically is just a fancy way to invoke operator>>. If you have real binary data in your stream, that will fail. Do you have real binary data in your file? Or are the integers stored as strings?

If you need to read real binary integers, what you need is either istream.read() or using the stream buffer object directly.

笑饮青盏花 2024-08-03 00:58:46

另一种方法与阿列克谢·马利斯托夫的回答相同:

#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;
}

A different way to do the same as Alexey Malistov's answer:

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文