Загрузите бинарный файл, используя fstream - PullRequest
3 голосов
/ 20 июля 2009

Я пытаюсь загрузить двоичный файл с помощью 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 для загрузки файла, но я хочу использовать итератор и operator>> для загрузки частей файла. Это возможно? Почему приведенный выше код не работает?

Ответы [ 4 ]

3 голосов
/ 21 июля 2009
  1. istream_iterator хочет basic_istream в качестве аргумента.
  2. невозможно перегрузить operator>> внутри basic_istream класса.
  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;
}
0 голосов
/ 18 июня 2010

Другой способ сделать то же, что и ответ Алексея Малистова:

#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;
}
0 голосов
/ 21 июля 2009

Вы можете перезагрузить оператор >>, чтобы правильно прочитать целые числа. Конечно, все, что он будет делать, это прочитать () 4 байта Но это то, что все остальные операторы в конечном итоге делают в любом случае.

Вот пример (без проверки ошибок, при условии, что endianess совпадает с текущим компилятором и т. Д.)

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

Подгонка под ваш собственный набор целых чисел (возможно, придется читать по одному байту за раз и сдвигать их, смотреть на файл в шестнадцатеричном редакторе, если вы не знаете порядок байтов), добавлять проверку ошибок, и вы должны возможность использовать существующий код.

РЕДАКТИРОВАТЬ: ах, и да, убедитесь, что эти тени предоставляют оператор stl, который читает целое число - возможно, придется извлечь свой собственный класс из потока, который вы используете, и использовать его вместо std :: istream & in, просто чтобы компилятор знал кого проверять первым.

0 голосов
/ 20 июля 2009

Главный вопрос, вероятно, заключается в том, что вы подразумеваете под "двоичным файлом". ios::binary только гарантирует, что объект istream не заменит переносимые на платформу символы новой строки на \ n. Ничего больше. Вам этого достаточно?

istream_iterator - это просто причудливый способ вызвать operator>>. Если в вашем потоке есть реальные двоичные данные, это не удастся. У вас есть реальные двоичные данные в вашем файле? Или целые числа хранятся в виде строк?

Если вам нужно прочитать реальные двоичные целые числа, вам нужно либо istream.read(), либо напрямую использовать объект потокового буфера.

...