Reading complex structures with streams

We believe that I have the following input format for the โ€œpair of floatsโ€ object: (first variable, second variable), for example. (1.0, 15.6).

What is the best way to read such structures? CI will use scanf ("(% f,% f)", & var1, & var2) - pretty nice, isn't it? (Yes, I know that it does not provide type safety, etc.)

But I only know one way to do this using C ++ streams:

float var1, var2; char tmp; cin >> tmp; cin >> var1; cin >> tmp; cin >> var2; cin >> tmp; 

It seems ugly, and it's just a couple of floats. So, is there an elegant way to do this? how

 cin >> "(" >> var1 >> ", " >> var2 >> ")"; 
+4
source share
3 answers

I would write an input statement:
Theft of FloatPair by @Seth Carnegie.

So the input now looks fine:

 FloatPair c; std::cin >> c; 

I would do it like this.

 std::istream& operator>>(std::istream& stream, FloatPair& out) { return stream >> I('(') >> out.a >> I(',') >> out.b >> I(')'); } 

Then I have an ignore object like this.
If you want, just cry. And to make the code a little easier, I call my I

 template<typename T> struct ignore { T ignoreItem; ignore(T const& x): ignoreItem(x) {} }; template<typename T> ignore<T> I(T const& x) { return ignore<T>(x);} // This is where I in the above code comes from. 

Then the input operator โ†’ for ignoring is as follows.

 std::istream& operator>>(std::istream& stream, ignore<T> const& test) { T next; if ((stream >> next) && (test.ignoreItem != next)) // if the stream already failed { stream.setstate(std::ios::badbit); // then don't change anything } // as it may confuse people return stream; } 

Specialization for string. To cope with the fact that the โ†’ operator on the line only reads the word.
Note: in scanf (), a space runs 1 or more spaces. Thus, it obeys the same rule if the input string has a space in it.

 template<> struct ignore<std::string> { std::vector<std::string> ignoreItemVector; ignore(std::string const& x) { // Split the input into a list of words to ignore. std::stringstream words(x); std::copy(std::istream_iterator<std::string>(words), std::istream_iterator<std::string>(words), std::back_inserter(ignoreItemVector) ); } }; template<> std::istream& operator>><std::string>(std::istream& stream, ignore<std::string> const& test) { // Specifically ignore each word. foreach(std::string const& loop, test.ignoreItemVector) { std::string next; if ((stream >> next) && (loop != next)) // if the stream already failed { stream.setstate(std::ios::badbit); // then don't change anything } // as it may confuse people } return stream; } 
+1
source

Perhaps Boost.Spirit is the closest you can find. Here is an example of how to use it to achieve exactly what you want.

+2
source

The way I do this is to create a class representing a pair of floats:

 class FloatPair { public: FloatPair() : a(), b() { } float a, b; }; 

And operator>> overloading for working with streams:

 istream& operator>>(istream& rhs, FloatPair& out) { rhs.ignore(256, '('); rhs >> out.a; rhs.ignore(256, ' '); rhs >> out.b; rhs.ignore(256, ')'); return rhs; } 

So you can just stream the FloatPair stream:

 FloatPair fp1, fp2; cin >> fp1 >> fp2; 

This is rather fragile, because if you have a lot of deviations from the format (float, float) in the input, it will break.

+1
source

All Articles