There is a question that is very similar in spirit here. Unfortunately that question didn't prompt much response - I thought I would ask a more specific question with the hope that an alternative method can be suggested.
I'm writing a binary file into std::cin
(with tar --to-command=./myprog
).
The binary file happens to be a set of floats and I want to put the data into std::vector<float>
- ideally the c++ way.
I can generate a std::vector<char>
very nicely (thanks to this answer)
#include <fstream>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
int
main (int ac, char **av)
{
std::istream& input = std::cin;
std::vector<char> buffer;
std::copy(
std::istreambuf_iterator<char>(input),
std::istreambuf_iterator<char>( ),
std::back_inserter(buffer)); // copies all data into buffer
}
I now want to transform my std::vector<char>
into a std::vector<float>
, presumably with std::transform
and a function that does the conversion (a char[2]
to a float
, say). I am struggling however, because my std::vector<float>
will have half as many elements as std::vector<char>
. If I could iterate with a stride of 2 then I think I would be fine, but from the previous question it seems that I cannot do that (at least not elegantly).
I would write my own class that reads two chars and converts it to float.
struct FloatConverter
{
// When the FloatConverter object is assigned to a float value
// i.e. When put into the vector<float> this method will be called
// to convert the object into a float.
operator float() { return 1.0; /* How you convert the 2 chars */ }
friend std::istream& operator>>(std::istream& st, FloatConverter& fc)
{
// You were not exactly clear on what should be read in.
// So I went pedantic and made sure we just read 2 characters.
fc.data[0] = str.get();
fc.data[1] = str.get();
retun str;
}
char data[2];
};
Based on comments by GMan:
struct FloatConverterFromBinary
{
// When the FloatConverterFromBinary object is assigned to a float value
// i.e. When put into the vector<float> this method will be called
// to convert the object into a float.
operator float() { return data }
friend std::istream& operator>>(std::istream& st, FloatConverterFromBinary& fc)
{
// Use reinterpret_cast to emphasis how dangerous and unportable this is.
str.read(reinterpret_cast<char*>(&fc.data), sizeof(float));
retun str;
}
float data;
};
Then use it like this:
int main (int ac, char **av)
{
std::istream& input = std::cin;
std::vector<float> buffer;
// Note: Because the FloatConverter does not drop whitespace while reading
// You can potentially use std::istream_iterator<>
//
std::copy(
std::istreambuf_iterator<FloatConverter>(input),
std::istreambuf_iterator<FloatConverter>( ),
std::back_inserter(buffer));
}
It seems to me that the best answer is to write a pair of your own iterators that parse the file the way that you want. You could change std::vector<char>
to std::vector<float>
and use the same streambuf
iterators provided the input was formatted with at least one space between values.
use boost range adaptors:
boost::copy(istream_range(input)|stride(2),back_inserter(buffer));
you might need to write your own istreambuf_iterator
, which is trivial.
来源:https://stackoverflow.com/questions/5424093/how-to-iterate-into-a-smaller-container-i-e-stride-1