3

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).

Community
  • 1
  • 1
Tom
  • 5,019
  • 1
  • 26
  • 43
  • 1
    Why read in `char`'s? Why not `std::string`'s? It's unclear what you want to be able to input, and what you want to accomplish. – GManNickG Mar 24 '11 at 19:01
  • @GMan, I want to finish with a std::vector from a binary file inputted into cin - I can then use a library function to decode the std::vector. If proceeding via stings is easier then thats fine (It wasn't clear to me how to do that with cin). – Tom Mar 24 '11 at 19:06
  • @Tom: What is the format of the binary file? – GManNickG Mar 24 '11 at 19:07
  • @GMan Its a gnuplot binary file - Its a long set of floats but not all the floats are data, for example, the first one is the number of floats (an integer expressed as a float) – Tom Mar 24 '11 at 19:09
  • @Tom: Is endianness a concern? – GManNickG Mar 24 '11 at 19:12
  • std::string into float: http://stackoverflow.com/questions/1012571/stdstring-to-float-or-double – karlphillip Mar 24 '11 at 19:15
  • @Gman, no, I wrote the binary file (using a different library) and all my pcs are the same. I now just now want to read it straight from tar (via cin) because the binaries prior to compression are rather large – Tom Mar 24 '11 at 19:16
  • @karlphillip, yep, the actual conversion isn't a problem, its how to make the char[2] or string in the first place that troubles me, from a vector – Tom Mar 24 '11 at 19:19
  • @Tom: Alright. In any case, Martin's approach is cleanest to me. – GManNickG Mar 24 '11 at 19:19

3 Answers3

5

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));
}
Martin York
  • 234,851
  • 74
  • 306
  • 532
  • +1 for the approach. To complete his answer, based on the comments on his question, it should read `sizeof(float)` `char`'s then `reinterpret_cast` them to a `float`, assuming endianness is not an issue. (If it is, swap some `char`'s around.) – GManNickG Mar 24 '11 at 19:16
  • Ah, cool, this looks exactly what I am looking for - let me try to work it out – Tom Mar 24 '11 at 19:22
  • Thank you @Martin, than you @Gman, I leave it to your imagination the horrible mish-mash of c and c++ that you have just replaced :) – Tom Mar 24 '11 at 19:29
0

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.

kirill_igum
  • 3,703
  • 5
  • 37
  • 68
0

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.

Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
Jon Trauntvein
  • 4,131
  • 6
  • 37
  • 65