I wanted to experiment with stringstream for an assignment, but I'm a little confused on how it works. I did a quick search but couldn't find anything that would answer my question.
Say I have a stream with a dynamic size, how would I know when to stop writing to the variable?
string var = "2 ++ asdf 3 * c";
stringstream ss;
ss << var;
while(ss){
ss >> var;
cout << var << endl;
}
and my output would be:
2
++
asdf
3
*
c
c
I'm not sure why I get that extra 'c' at the end, especially since _M_in_cur = 0x1001000d7 ""
You get the extra c at the end because you don't test whether the stream is still good after you perform the extraction:
while (ss) // test if stream is good
{
ss >> var; // attempt extraction <-- the stream state is set here
cout << var; // use result of extraction
}
You need to test the stream state between when you perform the extraction and when you use the result. Typically this is done by performing the extraction in the loop condition:
while (ss >> var) // attempt extraction then test if stream is good
{
cout << var; // use result of extraction
}
The while(ss) condition check in your code checks if the last read from the stream was successful or not. However, this check is going to return true even when you have read the last word in your string. Only the next extraction of ss >> var in your code is going to make this condition false since the end of the stream has been reached & there is nothing to extract into the variable var. This is the reason you get an extra 'c' at the end. You can eliminate this by changing your code as suggested by James McNellis.
There is also a member function good() which tests if the stream can be used for I/O operations. So using this the above code can be changed into
while(ss.good()) // check if the stream can be used for io
{
ss >> var; // attempt extraction <-- the stream state is set here
cout << var; // use result of extraction
}
Related
Today our Data Structure class asked us to implement Horner's Rule. I made it at last, but there is a small bug that upsets me very much. I use a while loop to read and save the coefficients of the polynomial, and I used another cin to read the value of the variable called x. The thing is, after I reach EOF to terminate reading the coefficients, the following cin part that reads the variable x does not execute. This puzzles me very much.
while(std::cin>>n){
coeff.append(n);
}
std::cout<<"Now enter the value";
std::cin>>x;
The program is correct in syntax, but it just skips the second cin which reads the variable x.
After you have terminated the stream, nothing more is added to it and there is nothing more to read.
You need a different "end of coefficients" marker.
The simplest way is to require all coefficients to be entered on one line:
std::string line;
if (std::getline(std::cin, line))
{
std::istringstream is(line);
int n = 0; // or whatever type you're using.
while (is >> n)
{
coeff.append(n);
}
std::cout << "Enter the value: ";
std::cin >> x;
// Evaluation code here.
}
else
{
// Possibly handle error
}
Other options include defining a "magic" coeffient as the marker, or take some invalid (i.e. non-numeric) input and then clear() the stream before continuing.
This is a question as to the philosophy (canonical design) of user-written C++ input stream extraction operators (>>).
Assume that on entry to the >> operator implementation (for a user-written class), the eof flag is already set for the input stream.
Should a user-written extraction operator (>>)
set the failure flag (because no instance of the desired object can be found)
should it just return to the caller with the eof flag still set.
If the second approach is used, it implies that the caller must always check the eof flag before any attempt is made to invoke the >> operator. The reason is that the >> operator might successfully extract an instance of the desired class and set the eof flag.
The original code follows. Based on the comments below, this code appears to be wrong. If eof is already set on input, the extraction operator will simply return with eof still set. It appears that if eof is set, but bad and fail are not set, then an extraction of a string should be done to set the fail bit. Of course, the fail bit can be set directly.
/* Implement the C/C++ >> (stream input) operator as a non-member
function */
std::istream &operator>>(std::istream& is, DecNumber &val) {
DecContext context{DecContext::defInit};
uint32_t status;
/* The true value below prevents whitespace from being skipped */
std::istream::sentry s(is, true);
std::string inStr;
/* Check if the input stream is in a good state. Just return to the
caller if the input stremm is not in a good state. The caller
must handle this condition. */
if(!s)
return is;
/* Get a string from the input stream. This string is converted to
a DecNumber below. Just return to the caller if this step causes
any stream related errors. Note that reaching the end of the
input is not a stream related error here. A decimal number might
be the absolute last thing in the stream. */
is >> inStr;
if (is.bad() || is.fail())
return is;
/* Try to convert the string to a DecNumber using the default context
value */
decNumberFromString(val.getDecVal(), inStr.c_str(), context.getDecCont());
status = context.DecContextGetStatus();
/* Remove a few status bits we don't care about */
status &= ~(DEC_Inexact + DEC_Rounded);
if (status)
is.setstate(std::ios_base::failbit);
return is;
}
You should implement solution 1.
When in doubt, just look at what's already being done. As you can see below, the fail bit is being set if we try to read from a stream in EOF state.
Note that EOF is not the only way to fail though. Try setting std::string vals = "52 43 A"; in the code below.
failbit should be set if for any reason, operator>> doesn't actually stream a value. EOF is just one of those reasons.
#include <sstream>
#include <iostream>
#include <string>
void print_stream (std::istream & print_me, int const & i)
{
std::cout << "i: " << i << "\n";
std::ios_base::iostate bits = print_me.rdstate();
std::cout << "good: " << (bits & std::ios_base::goodbit) <<
", bad: " << (bits & std::ios_base::badbit) <<
", fail: " << (bits & std::ios_base::failbit) <<
", eof: " << (bits & std::ios_base::eofbit) << "\n";
std::cout << "\n----------------------------\n\n";
}
int main (void)
{
std::string vals = "52 43";
std::istringstream iss(vals);
int i;
iss >> i;
print_stream (iss, i);
iss >> i;
print_stream (iss, i);
iss >> i;
print_stream (iss, i);
iss >> i;
print_stream (iss, i);
return 0;
}
Outputs
$ ./a.exe
i: 52
good: 0, bad: 0, fail: 0, eof: 0
----------------------------
i: 43
good: 0, bad: 0, fail: 0, eof: 2
----------------------------
i: 43
good: 0, bad: 0, fail: 4, eof: 2
----------------------------
i: 43
good: 0, bad: 0, fail: 4, eof: 2
----------------------------
Note that the typical read pattern loop is some variation of...
while (input >> var >> var2 >> var3)
{
// failbit is not set. All reads succeeded.
// Do Stuff
}
If you need to detect whether the fail happened at some point during reading of multiple values then yea, you need to be a little more sophisticated and do some testing like...
while (true)
{
if (input >> var)
{
// We successfully read first value
if (input >> var2 >> var3)
{
// We succesfully read all the values!
// Do stuff
}
else
{
ErrorLog ("Partial line read!");
break;
}
else
{
// Nothing else to read
break;
}
}
"If the second approach is used, it implies that the caller must always check the eof flag before any attempt is made to invoke the >> operator."
No, why do you think they need to do so?
"Should a user-written extraction operator (>>) set the failure flag (because no instance of the desired object can be found) or should it just return to the caller with the eof flag still set."
The latter option of course, you're not supposed to manage stream states in overloaded extraction operators, unless you add your own validation rules (e.g. for expecting specific character patterns with the std::string field). It will usually be done correctly with the sub extraction operations that the overloaded operator uses.
Supposed you have something like follows:
struct MyType {
std::string field1;
int field2;
double field3;
}
std::istream& operator>>(std::istream& is, MyType& myinstance) {
is >> field1;
is >> field2;
is >> field3;
return is;
}
Each of the extractions will set the fields to their default constructed values, in case the operator>>() fails, because the stream is in eof() state, and the value will be left in it's original state for the field that was attempted to extract.
I actually don't see a need to have any additional check for eof() or setting the stream to fail() state in your overloaded input operator.
The client (caller) will simply use something like e.g.
std::ifstream input("MyFile.txt");
std::vector<MyType> allObjects;
MyType curObject;
while(input >> curObject) {
allObjects.push_back(curObject);
}
You see, no need to check for input.eof() anywhere.
I need a self defined extractor (operator>>) to read a specific string
into my own datatype.
The problem is that the requirements for the string are large.
Hence the easiest way is probably to read the whole string from the istream
and then check if all requirements are fulfilled.
My Problem is if the string is not valid.
Up to my knowledge it is common in C++ that the stream is unchanged.
What is best practice to recover the istream in this case?
Is the exception handling in the following example enough?
std::istream& operator>>(std::istream& is, Foo& f)
{
std::string str;
if (is >> str)
{
// check if string is valid
if ( is_valid( str ) )
{
// set new values in f
}
else
{
// recover stream
std::for_each(str.rbegin(), str.rend(),
[&] (char c)
{
is.putback(c);
});
// ste failbit
is.clear(std::ios_base::failbit);
}
}
return is;
}
And what about std::getline() instead of is >> str ? Are there other pitfalls?
Thanks
Marco
You can't get streams back to the initial position where you started reading, at least not in general. In theory, you can put back characters or seek to a location where you had been before but many stream buffers don't support putting back characters or seeking. The standard library gives some limited guidance but it deals with rather simple types, e.g., integers: the characters are read as long as the format matches and it stops just there. Even if the format matches, there may be some errors which could have been detected earlier.
Here is a test program demonstrating the standard library behavior:
#include <iostream>
#include <sstream>
void test(std::string const& input)
{
std::istringstream in(input);
int i;
std::string tail;
bool result(in >> i);
in.clear();
std::getline(in, tail);
std::cout << "input='" << input << "' "
<< "fail=" << std::boolalpha << result << " "
<< "tail='" << tail << "'\n";
}
int main()
{
test("10 y");
test("-x y");
test("0123456789 x");
test("123456789012345678901234567890 x");
}
Just to explain the four test cases:
Just to make sure the test does what it is meant to do, the first input is actually OK and there is no problem.
The second input starts with a character matching the format followed by something not matching and reading stops right after the '-' character.
The third test reads an int using octal numbers. The failure could have been detected upon the character '8' but both the '8' and the '9' are consumed and the input fails.
The last example results in an overflow which could be detected before all digits are read but still all digits are read.
Based on that, I'd think there wouldn't be an expectation to reset the stream to the original position when semantics checks on a well-formed input fail.
I have a question on the stream behavior, see the following example. What I was expecting is, since there are only 5 chars in the string, and stream read will get stuck as I am trying to read 10 chars. Instead, the output is "hellooooo" ... the last char get repeated.
My questions are two folds: first, why? second, is there anyway to make stream behave as if no more repeating of last char?
#include <sstream>
#include <iostream>
using namespace std;
int main(void) {
char c;
string msg("hello");
istringstream iss(msg);
unsigned int i = 0;
while (i < 10) {
iss >> c;
cout << c;
i++;
}
cout << endl;
return 0;
}
What you see is the result of reading form a stream in an erronous state. When you read past the last element in the stream (this being a string stream), the stream becomes erroneous and any other attempt to read from it will fail (and leave the extraction variable untouched).
You will have to check if the extraction operation succeeded before reading further:
if (iss >> c) {
// succeess
} else {
// failed to extract, handle error
}
Were you to use a stream connected to the console (for an example) your call to >> would have blocked as you expected. The behavior of stringstream is different (you cannot expect to micraculously contain more data)
The reason is that when you've read to the end of the stream, all attempts to read after that just fail, leaving the last value read in your c.
If you want to read at most 10 characters:
while (i < 10 && is >> c) {
cout << c;
i++;
}
This works because a stream can be converted to bool, and it's true if the stream is in a "good" state.
"the last char get repeated"
When iss >> c fails, c stays unmodified.
Check whether extraction of value succeeded by directly evaluating this expression: if (iss >> c), but don't even think about calling iss.good(). Check this answer and also have a look at:
How does that funky while (std::cin >> foo) syntax work?
Why does my input seem to process past the end of file?
I'm doing a console app, I'm passing an integer to the app and it works ok, but if I pass a letter, it goes crazy,
int opt=0;
std::cout<<"Pick lang:"<<'\n';
std::cout<<"1.[es-ES]:"<<'\n';
std::cout<<"2.[en-US]:"<<'\n';
std::cin >> opt;
while(opt<1 || opt>2)
{
std::cout<<"\nERROR!"<<'\n';
std::cout<<"Pick lang again:"<<'\n';
std::cout<<"1.[es-ES]:"<<'\n';
std::cout<<"2.[en-US]:"<<'\n';
std::cin >> opt;
}
I tried to use isdigit() but I get the same result. Thanks
After performing cin >> extraction, you want to check if the cin stream is still good or not. If you expect cin to extract a number but it gets something else instead, eg. like a letter, then the stream will be set to a bad state and that's why you see it 'going crazy'.
What you have to do is after input, check if cin is still good. If it's in a bad state, you need to clear its flags and then remove out any of the junk data in the stream. If you don't, then subsequent uses of cin will simply fail to function.
Taking your code snippet for example, you can change it to something like this:
int opt = 0;
bool inputGood = false;
do
{
std::cout << "Pick lang again:" << '\n';
std::cout << "1.[es-ES]:" << '\n';
std::cout << "2.[en-US]:" << '\n';
inputGood = std::cin >> opt;
if(!inputGood)
{
std::cout << "\nERROR! Invalid choice." << '\n';
cin.clear();
while( cin.get() != '\n' );
}
}while(!inputGood || opt < 1 || opt > 2);
Edit: whoops minor error in the cin error handling. Corrected and should be working now. :)
The problem is that the call std::cin >> opt is failing to parse the character and returns immediatly (without consuming the buffer), then it finds the same contents and fail....
You should check the result of the operation and react to it. One possibility would be checking the fail bit (std::cin.fail()) and failing the whole operation or consuming parts of the buffer (maybe a a single character, maybe more, depending on how you want the application to behave).
The simplest thing would probably be not reading into a number, but rather a character, and then comparing with the expected character:
char opt = 0;
do {
// prompt user for input
if (! (std::cin >> opt) ) {
// io error, report and bail out
break;
}
} while ( opt != '0' && opt != '1' );
Reading in numbers directly is
problematic
If std::cin is presented with input it
cannot process, std::cin goes into a
"fail" state The input it cannot
process is left on the input stream.
All input will be ignored by std::cin
until the "fail" state is cleared:
std::cin.clear()
A routine that reads
a number directly should:
Read in the
number
Check to see that the input
stream is still valid
If the input
stream is not good (!std::cin)
Call
std::cin.clear() to take the stream
out of the "fail" state.
Remove from
the stream the input that caused the
problem: std::cin.ignore(...)
Get the
input again if appropriate or
otherwise handle the error
more info here: http://www.augustcouncil.com/~tgibson/tutorial/iotips.html
When you insert a letter this happens:
operator>> extracts characters from the stream and try to convert them to a number;
it fails in the conversion, so it sets the stream state to ios::failbit and returns; opt probably is untouched (the standard delegates this stuff to the locale library, which is a zone of C++ that I never really understood - for the brave enough, it's at §22.2.2.1.2);
since it returned and (probably) opt is left as it is, the loop continues;
when the execution returns to std::cin >> opt;, operator>> sees that the state is still ios::failbit, so it doesn't even try to extract anything;
goto 3.
To fix the problem, you should clean the error state and remove the "wrong" characters from the input buffer. Since you probably don't want to add all that code to every cin>>, it's useful to create a function to deal with this common problem; personally, I created this little header (AcquireInput.hpp) that has proven useful many times:
#ifndef ACQUIREINPUT_HPP_INCLUDED
#define ACQUIREINPUT_HPP_INCLUDED
#include <iosfwd>
#include <limits>
#include <string>
template<typename InType> void AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString, InType & Result)
{
do
{
Os<<Prompt.c_str();
if(Is.fail())
{
Is.clear();
Is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Is>>Result;
if(Is.fail())
Os<<FailString.c_str();
} while(Is.fail());
}
template<typename InType> InType AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString)
{
InType temp;
AcquireInput(Os,Is,Prompt,FailString,temp);
return temp;
}
/* Usage example:
//1st overload
int AnInteger;
AcquireInput(cout,cin,"Please insert an integer: ","Invalid value.\n",AnInteger);
//2nd overload (more convenient, in this case)
int AnInteger=AcquireInput(cout,cin, "Please insert an integer: ","Invalid value.\n");
*/
#endif