Построение вектора с помощью istream_iterators
Я помню, как однажды увидел умный способ использования итераторов для чтения всего двоичного файла в вектор. Это выглядело примерно так:
#include <fstream>
#include <ios>
#include <iostream>
#include <vector>
using namespace std;
int main() {
ifstream source("myfile.dat", ios::in | ios::binary);
vector<char> data(istream_iterator(source), ???);
// do stuff with data
return 0;
}
Идея состоит в том, чтобы использовать конструктор диапазона tтератора vector
, передавая итераторы ввода, которые определяют весь поток. Проблема в том, что я не уверен, что передать для итератора конца.
Как вы создаете istream_iterator
для конца файла? Я полностью забываю эту идиому?
Ответы
Ответ 1
Вы хотите std::istreambuf_iterator<>
, для сырого ввода. std::istream_iterator<>
предназначен для форматированного ввода. Что касается конца файла, используйте конструктор по умолчанию iterator.
std::ifstream source("myfile.dat", std::ios::binary);
std::vector<char> data((std::istreambuf_iterator<char>(source)),
std::istreambuf_iterator<char>());
Отредактировано, чтобы удовлетворить С++ наиболее неприятный синтаксический анализ. Спасибо, @UncleBens.
Ответ 2
В С++ 11 можно было:
std::ifstream source("myfile.dat", std::ios::binary);
std::vector<char> data(std::istreambuf_iterator<char>(source), {});
Эта более короткая форма позволяет избежать наиболее неприятной проблемы синтаксического анализа из-за аргумента {}
, который устраняет двусмысленность этого аргумента или формального параметра.
@wilhelmtell ответ также может быть обновлен, чтобы избежать этой проблемы, приняв инициализатор привязки для data
. По-моему, использование {}
более простое и не меняет форму инициализации.
ИЗМЕНИТЬ
Или, если у нас std::lvalue
(и, возможно, std::xvalue
вместо std::move
):
#include <vector>
#include <fstream>
template <typename T>
constexpr T &lvalue(T &&r) noexcept { return r; }
int main() {
using namespace std;
vector<char> data(
istreambuf_iterator<char>(lvalue(ifstream("myfile.dat", ios::binary))),
{}
);
}