The goal is to read 16 bit signed integers from a binary file. First, I open the file as an ifstream, then I would like to copy each numbers into a vector using istream_iterator and the copy algorithm. I dont' understand what's wrong with this snippet:
int main(int argc, char *argv[]) {
std::string filename("test.bin");
std::ifstream is(filename);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::noskipws(is);
std::vector<int16_t> v;
std::copy(
std::istream_iterator<int16_t>(is),
std::istream_iterator<int16_t>(),
std::back_inserter(v)
);
//v is still empty
}
This code produces no error but the vector remains empty after the call to std::copy. Since I'm opening the file in the standard input mode ("textual" mode), I was expecting istream_iterator to work even if the file is binary. Of course there's something I'm missing about the behavior of this class.
First off, to read a binary file with ifstream
, you need to open the file in binary
mode, not text mode (the default). Otherwise, read operations may mis-interpret linebreak bytes and translate them between platform encodings (ie, CRLF->LF, or vice versa), thus corrupting your binary data.
Second, istream_iterator
uses operator>>
, which reads and parses formatted text by default, which is not what you want when reading a binary file. You need to use istream::read()
instead. However, there is no iterator wrapper for that (but you can write your own if needed).
Try this instead:
int main(int argc, char *argv[]) {
std::string filename = "test.bin";
std::ifstream is(filename, std::ifstream::binary);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::vector<int16_t> vec;
int16_t value;
while (is.read(reinterpret_cast<char*>(&value), sizeof(value))) {
// swap value's endian, if needed...
vec.push_back(value);
}
// use vec as needed...
return 0;
}
That being said, if you really want to use istream_iterator
for a binary file, then you would have to write a custom class/struct to wrap int16_t
, and then define an operator>>
for that type to call read()
, eg:
struct myInt16_t {
int16_t value;
operator int16_t() const { return value; }
};
std::istream& operator>>(std::istream &is, myInt16_t &v) {
if (is.read(reinterpret_cast<char*>(&v.value), sizeof(v.value))) {
// swap v.value's endian, if needed...
}
return is;
}
int main(int argc, char *argv[]) {
std::string filename = "test.bin";
std::ifstream is(filename, std::ifstream::binary);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::noskipws(is);
std::vector<int16_t> vec;
std::copy(
std::istream_iterator<myInt16_t>(is),
std::istream_iterator<myInt16_t>(),
std::back_inserter(vec)
);
// use vec as needed...
return 0;
}