getting rid of unwanted characters in the input

  • Thread starter subramanian100in
  • Start date
S

subramanian100in

I want to do the following:
read an integer
do some processing
ask the user if he wants to continue.
if yes, continue the above.

For this I wrote the following code:

#include <cstdlib>
#include <iostream>
#include <cctype>

using namespace std;

inline void process(int arg)
{
cout << "From process() function: " << arg << endl;
}

int main()
{
char flag;

do
{
cout << "Enter an integer: ";
int arg;

if (!(cin >> arg))
break;

process(arg);

cout << "Continue(Y/N) ? ";

} while (cin >> flag && toupper(flag) == 'Y');

return EXIT_SUCCESS;
}

This program compiles fine with g++3.4.3 and works fine as long as
correct input is entered. However this program does not work correctly
for wrong test input explained as follows:

Now consider the following scenarios.
Scenario 1: Suppose I run this program. It asks:
Enter an integer:
For this, suppose I enter, '10Y'. Then it displays:
From process() function: 10
Continue(Y/N) ? Enter an integer:
What happens is that for 'Continue(Y/N) ?', it does not wait for the
input because the 'Y' entered(in '10Y') for the previous integer, is
taken as the input for the 'Continue' flag. Here How do I get rid of
the input remaining after reading the integer ? ie How to remove all
the characters remaining after the integer ?

Scenario 2: Suppose I run this program. It asks:
Enter an integer:
For this, suppose I enter, '10'. Then it displays:
From process() function: 10
Continue(Y/N) ?
For this suppose I enter 'Y20'.
Then it displays:
Enter an integer: From process() function: 20
Continue(Y/N) ?
Here it does not wait for the integer input during the second pass.
What happens is that the 'Y' in 'Y20' is taken for 'Continue' flag and
the remaining integer '20' is taken for the subsequent read of the
integer value. Here how do I get rid of all the characters after the
first character for the flag ?

Is there a better program for the problem I stated in the beginning of
this post? Kindly provide the code.

Thanks
V.Subramanian
 
F

Francesco S. Carta

on said:
I want to do the following:
read an integer
do some processing
ask the user if he wants to continue.
if yes, continue the above.

For this I wrote the following code:

#include<cstdlib>
#include<iostream>
#include<cctype>

using namespace std;

inline void process(int arg)
{
cout<< "From process() function: "<< arg<< endl;
}

int main()
{
char flag;

do
{
cout<< "Enter an integer: ";
int arg;

if (!(cin>> arg))
break;

process(arg);

cout<< "Continue(Y/N) ? ";

} while (cin>> flag&& toupper(flag) == 'Y');

return EXIT_SUCCESS;
}

This program compiles fine with g++3.4.3 and works fine as long as
correct input is entered. However this program does not work correctly
for wrong test input explained as follows:

Now consider the following scenarios.
Scenario 1: Suppose I run this program. It asks:
Enter an integer:
For this, suppose I enter, '10Y'. Then it displays:
From process() function: 10
Continue(Y/N) ? Enter an integer:
What happens is that for 'Continue(Y/N) ?', it does not wait for the
input because the 'Y' entered(in '10Y') for the previous integer, is
taken as the input for the 'Continue' flag. Here How do I get rid of
the input remaining after reading the integer ? ie How to remove all
the characters remaining after the integer ?

I would do it so that instead of directly reading from cin to the
variable, I would ask cin for a line, like this:

string str;
getline(cin, str);

then I would create a stringstream from that string:

stringstream stream(str);

and I would extract the value from there:

stream >> arg;

discarding all the rest.

When I'd need to ask for a confirmation I would do just like above, but
right after having gotten the string, I would check it in a way like this:

if(str == "Y") { //...

or something more permissive like you did, allowing for a lowercase "y"
or for longer input like "yes" - by checking only the first char of the
line.
 
J

Jorgen Grahn

I would do it so that instead of directly reading from cin to the
variable, I would ask cin for a line, like this:

string str;
getline(cin, str);

I would do that too, but then I'd feed that line to strtol() or one of
its cousins, and use its mechanisms to make sure the line was just
whitespace and an integer, short enough not to overflow an int.
Probably I would have to do other checks too to do stuff like accepting
trailing whitespace.
then I would create a stringstream from that string:

stringstream stream(str);

and I would extract the value from there:

stream >> arg;

discarding all the rest.

For an example that would be OK I guess, but in general I don't like
programs which silently discard input they don't understand.

(Few non-example programs these days have "enter an integer" prompts
these days. Most have at least command-line editing, history ...)

/Jorgen
 
F

Francesco S. Carta

I would do that too, but then I'd feed that line to strtol() or one of
its cousins, and use its mechanisms to make sure the line was just
whitespace and an integer, short enough not to overflow an int.
Probably I would have to do other checks too to do stuff like accepting
trailing whitespace.


For an example that would be OK I guess, but in general I don't like
programs which silently discard input they don't understand.

(Few non-example programs these days have "enter an integer" prompts
these days. Most have at least command-line editing, history ...)

I agree with you that my example can be improved, making a more flexible
and correct process out of it, but it was simple and minimal on purpose.
The OP asked explicitly about discarding the unwanted input, and since
the OP is also a frequent poster who interacts with the respondents, I
thought that giving a minimal hint would have been more than enough -
besides, I've hinted about making the program more flexible when I wrote
about accepting "Y", "y" and something longer as "yes" as valid input,
but I didn't really feel like answering unasked questions and presenting
unrequested details more than that, not in this case at least.
 
J

Jorgen Grahn

I agree with you that my example can be improved, making a more flexible
and correct process out of it, but it was simple and minimal on purpose.
The OP asked explicitly about discarding the unwanted input,

Ah, I missed that, but if I had seen it I would only have stressed
that even more. Not to critizise you, but to make him avoid a user
interface design which leads to pain.
and since
the OP is also a frequent poster who interacts with the respondents, I
thought that giving a minimal hint would have been more than enough -
besides, I've hinted about making the program more flexible when I wrote
about accepting "Y", "y" and something longer as "yes" as valid input,
but I didn't really feel like answering unasked questions and presenting
unrequested details more than that, not in this case at least.

Ah, you don't have to motivate your replies. Some reply to the direct
questions, some plan to continue a dialogue, and some question the
assumptions behind the question. That's just how it it. The people
reading it can put the pieces together.

/Jorgen
 
F

Francesco S. Carta

Ah, I missed that, but if I had seen it I would only have stressed
that even more. Not to critizise you, but to make him avoid a user
interface design which leads to pain.

No problem, I actually agree with your point of view, and standing some
other context, I would have done just the same remarks.
Ah, you don't have to motivate your replies. Some reply to the direct
questions, some plan to continue a dialogue, and some question the
assumptions behind the question. That's just how it it. The people
reading it can put the pieces together.

Full ack. My point, as a whole, was moreover to express my agreement
about the need to improve the program at hand.

The motivation comes straight from the "unrequested details" point I
cited above. I mentioned it because it's a bad habit of mine to overload
a post with them - something I try to avoid when I realize it - but I
motivated my post exactly for the same bad habit - proof of the fact
that I don't always realize when I'm overloading.

Now I'm overloading, but this very time it done on purpose ;-)
 
J

James Kanze

[...]
I would do that too, but then I'd feed that line to strtol() or one of
its cousins, and use its mechanisms to make sure the line was just
whitespace and an integer, short enough not to overflow an int.
Probably I would have to do other checks too to do stuff like accepting
trailing whitespace.

You mean something like:
static boost::regex const isInt( "\\s*[+-]?\\d+\\s*" );
if ( !regex_match( line, isInt ) ) {
std::cerr << "I wanted an integer" << std::endl;
} else {
std::istringstream toParse( line ):
int i;
toParse >> i;
// ...
}

In this case, it's almost as easy to parse directly:
std::istringstream toParse( line );
int i;
toParse >> i >> std::ws;
if ( !toParse || toParse.get() != EOF )
std::cerr << "I wanted an integer" << std::endl;
} else {
// ...
}
But the regex solution will often be a lot simpler than testing
a lot of different error conditions.

[...]
For an example that would be OK I guess, but in general
I don't like programs which silently discard input they don't
understand.

I agree, but having found and reported an error, you still have
to discard the rest of the line if you want to advance.
(Few non-example programs these days have "enter an integer"
prompts these days. Most have at least command-line editing,
history ...)

Something similar is often required to parse command line
arguments. Or lines in a configuration file. Or any number of
other outputs. (About the only program I've ever seen that
asked for input like the above---and that goes back a long, long
way---was the old Unix units: "You have?", "You want?".)
 
J

Jorgen Grahn

(e-mail address removed), India <[email protected]>, on
09/08/2010 07:16:19, wrote:
[...]
string str;
getline(cin, str);
I would do that too, but then I'd feed that line to strtol() or one of
its cousins, and use its mechanisms to make sure the line was just
whitespace and an integer, short enough not to overflow an int.
Probably I would have to do other checks too to do stuff like accepting
trailing whitespace.

You mean something like:
static boost::regex const isInt( "\\s*[+-]?\\d+\\s*" );
if ( !regex_match( line, isInt ) ) {
std::cerr << "I wanted an integer" << std::endl;
} else {
std::istringstream toParse( line ):
int i;
toParse >> i;
// ...
}

No, I meant something like "if strtol() says it found a number, and yet
says the first non-digit character wasn't '\0', loop over that tail
and ensure everything isspace()".

Or I could probably declare "trailing whitespace is illegal here",
skip that step, and get away with it. Allowing whitespace *before* the
number is more useful and also easier -- strtol() handles that
automatically.
In this case, it's almost as easy to parse directly:
std::istringstream toParse( line );
int i;
toParse >> i >> std::ws;
if ( !toParse || toParse.get() != EOF )
std::cerr << "I wanted an integer" << std::endl;
} else {
// ...
}

I think I've mentioned it before: I'm not friends with the istreams as
they are used here. Sorry! (Part of the reason is I guess that I have
an excellent manual page for strtol(), but only TC++PL for istream.)

But isn't it also clumpsy to create an istringstream just so you can
treat the end-of-line as an actual end of something?
But the regex solution will often be a lot simpler than testing
a lot of different error conditions.

In some cases, yes. Here I don't quite like it, because it gives you a
duplicate definition of the syntax of an integer: the regex and
istream >> int. You might forbid some syntax that the '>>' would have
accepted -- similar to those annyoing web sites which claim my mail
address is incorrect because it contains a '+'.
[...]
For an example that would be OK I guess, but in general
I don't like programs which silently discard input they don't
understand.

I agree, but having found and reported an error, you still have
to discard the rest of the line if you want to advance.

Yes. That's one reason to read line-by-line.
Something similar is often required to parse command line
arguments. Or lines in a configuration file. Or any number of
other outputs.

Sure, parsing text is not a thing of the past. I was trying to say that
these kinds of prompts are usually bad user interface design. For
example, you see Unix newbies using them where an experienced
programmer chooses command-line options.

/Jorgen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top