TITLE: fscanf equivalent with streams (Newsgroups: comp.lang.c++, 17 May 2000) FUNGUS: fungus > What's the cleanest, nicest way to do this, but with a stream? > if (fscanf(input, "%lf , %lf , %lf", &a, &b, &c) == 3) { KUEHL: Dietmar Kuehl That's simple: if (input >> a >> b >> c) { The expression in the 'if' statement will be 'true' if none of the following occurs: - 'input' gets corrupted, eg. it was not opend or something really bad happens to the stream (eg. a failure to allocate memory). In this case 'badbit' is set for 'input' and a conversion of the stream to a Boolean yields 'false'. - When trying to read one of the objects an unexpected character was encountered, the conversion fails. For example, during numeric conversion, if some character not fitting into the format of a number eg. a leading 'q' is encountered this would be a conversion error and 'failbit' is set. If 'failbit' is set, the conersion of the stream to a Boolean also yields 'false'. - When end of the file is reached before a value could be converted from the characters, the conversion fails and 'failbit' is set. In addition, 'eofbit' is also set. If end of file is reached after enough characters for a conversion could be extracted, 'eofbit' is set but 'failbit' is not set. If only 'eofbit' is set, the conversion to a Boolean value yields 'true. > But I'm havine trouble getting it to be neat and tidy (then again, > all stream io seems messy compared to printf/scanf. I know it's > "better", but why should it be so messy?) To me the IOStream version looks less complicated. Of course, this is just my opinion... ===================================================================== TITLE: fscanf equivalent with streams (part II) (Newsgroups: comp.lang.c++, 17 May 2000) FUNGUS: fungus >> What's the cleanest, nicest way to do this, but with a stream? >> if (fscanf(input, "%lf , %lf , %lf", &a, &b, &c) == 3) { FUNGUS: fungus > What about the commas...and the whitespace...? KUEHL: Dietmar Kuehl Good point! I haven't noticed the commas in the C code... Whitespace isn't a problem here because leading whitespace is automatically consumed. If you don't want to consume leading whitespace, you can setup the stream not consume it by clearing the 'std::ios_base::skipws' bit. In this case, you would use 'std::ws' modifier to explicitly skip whitespace. Basically, this is also the approach to skipping commas. First the code for specifically skipping commas, because it is somewhat simpler than the general case...: std::istream& comma(std::istream& in) { typedef std::char_traits traits; std::istream::sentry kerberos(in, true); if (in && in.rdbuf()->sgetc() == traits::to_int_type(',')) in.rdbuf()->sbumpc(); else if (in.rdbuf()->sgetc() == traits::eof()) in.setstate(std::ios_base::failbit | std::ios_base::eofbit); else in.setstate(std::ios_base::failbit); return in; } if (in >> a >> comma >> b >> comma >> c) { ... } Basically, a manipulator called 'comma' is created which just does the following: - First it checks that the stream is OK and if necessary a 'tie()'d 'std::ostream' is flushed: This is what is done with the object named 'kerberos' (the guard at the entrance to Hades). The second argument to the 'sentry' class is a flag indicating whether whitespace is to be skipped. Using 'true' suppresses skipping of whitespace. - If this was successful, the next character from the stream buffer associated with the stream is checked ('sgetc()') and if it is a ',', it is removed from the stream ('sbumpc()'). - If extracting a comma was not successful, the error flags are set up correspondingly: If end of file was reached, both 'failbit' and 'eofbit' are set, otherwise only 'failbit' is set. Now, we don't want to code the manipulator everytime we need to skip some character. Instead, we want a version working for all characters. By just sticking to one character, this can be achieved using basically the same code but without hardcoding the ',' and using a template argument instead: template std::istream& sl(std::istream& in) { typedef std::char_traits traits; std::istream::sentry kerberos(in, true); if (in && in.rdbuf()->sgetc() == traits::to_int_type(c)) in.rdbuf()->sbumpc(); else if (in.rdbuf()->sgetc() == traits::eof()) in.setstate(std::ios_base::failbit | std::ios_base::eofbit); else in.setstate(std::ios_base::failbit); return in; } if (in >> a >> sl<','> >> b >> sl<';'> >> c) { ... } Unfortunately, the template arguments to the functions do not mix well optically with the extraction operators :-( stdio also allows skipping of multiple letters with the special behavior of whitespace skipping all whitespace characters. This can also be implemented with a manipulator, although with a manipulator taking an argument: These are more involved. This is because the argument has to be stored somewhere. Here is the corresponding code: struct ss { ss(std::string const& s): str(s) {} std::string str; }; std::istream& operator>> (std::istream& in, ss const& s) { typedef std::char_traits traits; std::ctype const& ct = std::use_facet >(in.getloc()); std::istream::sentry kerberos(in, true); if (in) for (std::string::const_iterator it = s.str.begin(); it != s.str.end(); ++it) if (ct.is(ct.space, *it)) in >> std::ws; else { std::streambuf::int_type c = in.rdbuf()->sgetc(); if (c == traits::eof()) { in.setstate(std::ios_base::failbit | std::ios_base::eofbit); break; } if (c != traits::to_int_type(*it)) { in.setstate(std::ios_base::failbit); break; } in.rdbuf()->sbumpc(); } return in; } if (std::cin >> a >> ss(", b") >> b >> ss(", c") >> c) { ... } The use of this stuff still looks reasonable while the implementation does not really look that cute. ... but then, now that the code is written, it can just be used (well, you would need a version with a proper copyright since this version cannot be used due to the implicit copyright I'm holding on this code; however, if anybody is interested, I will pack it up in a header with a suitable copyright). I think the C++ code is still reasonable, as long as you don't have to write the manipulators yourself... It now has a similar level to the stdio version, though. _______________________________________________ cpptips mailing list http://cpptips.hyperformix.com