Code Newbie
News     Forums     Search     Members     Sign Up    

My Code Newbie
Username

Password

Articles/Snippets
ASP Classic
ASP.NET
C
C#
C++
HTML / CSS
Java
Javascript
Linux / BSD
Perl
PHP
Python
Ruby
SQL
VB 6
VB.NET

C.N. Friends
  Planet Rome

Link to Us!
Code Newbie
  Code Newbie
    forums
Old 04-17-2005, 12:48 PM   #1 (permalink)
teknomage1
Jack of all trades
 
teknomage1's Avatar
 
Join Date: Feb 2005
Location: Los Angeles
Posts: 598
teknomage1 is on a distinguished road
Send a message via AIM to teknomage1
User input

So I'm taking a C++ class right now and we haven't gotten to strings yet but we've had to write programs that take user input, often as integers. But if the user puts in something that's not an int the program goes into an infinite loop. What's the proper way to read input?
Code:
    int numRows;
    do {
          cout << "Enter an odd number between 1 and 19 : ";
          cin >> numRows;
     } while (numRows < 0 || numRows > 20 || !(numRows % 2) );
teknomage1 is offline   Reply With Quote
Old 04-17-2005, 02:15 PM   #2 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
Valmont is on a distinguished road
Here it is. Much harder then what you find on the net, but at least it is robust and thourough.
There is a lot left to play with. Have fun.
Code:
#include <iostream>
#include <string>
#include <sstream>
#include <cstdlib> //size_t
#include <cctype> //isdigit()

using namespace std;

//C++ allows conversion from a floating point to integer. But this function
//doesn't allow that. It wants "strict" (signed) integers only. 
bool string_to_strict_int(const string& sInLine, int& dest);

int main()
{
  string sNumInput;
  int myInt;
  
  //As long as the user doesn't enter a valid, strict (non-floating point)
  //(signed) integer, we keep on asking for one.
  while(string_to_strict_int(sNumInput, myInt) == false )
  {
    cout<<"Please enter an integer"<<endl;
    cout<<"-> : ";
    getline(cin, sNumInput);
  }
  
  //And here we have our (signed) integer at our disposal.
  cout<<myInt<<endl;
  
  cin.get();
  return 0;
}

bool string_to_strict_int(const string& sInLine, int& dest)
{
  bool bNonDigit = false;
  bNonDigit = false;
  
  if(sInLine[0] == '-' || isdigit(sInLine[0]) != 0)
  {
    for(std::size_t i = 1 ; i < sInLine.size(); ++i )
    {
      if( !isdigit(sInLine[i] ))
      {
        return false;
      }
    }      
  }
  
  istringstream  InStream( sInLine );
  InStream >> dest;
  if( !InStream )
  {
    //Oops, empty string found. That's not an integer either.
    return false;
  }
  else
  {
    //All went successfull. We had a strict integer value passed to this funct.
    return true;
  }  
}
__________________

Last edited by Valmont; 04-17-2005 at 03:56 PM.
Valmont is offline   Reply With Quote
Old 04-17-2005, 04:12 PM   #3 (permalink)
teknomage1
Jack of all trades
 
teknomage1's Avatar
 
Join Date: Feb 2005
Location: Los Angeles
Posts: 598
teknomage1 is on a distinguished road
Send a message via AIM to teknomage1
Thank you very much, Valmont.
teknomage1 is offline   Reply With Quote
Old 04-19-2005, 04:44 PM   #4 (permalink)
Feis
Registered User
 
Join Date: Apr 2005
Posts: 18
Feis is on a distinguished road
Well, here is an (ever so slightly) modified version. After first running some tests, I realized if you typed a space first, you tricked the entire if statement, as it was only looking for "-" or whether it was a digit. To fix this, I added another clause to the if statement, (isspace() from cctype). I also took cstdlib, and took out size_t for an int. Althoguh I got a warning from the compiler, no errors arised, although I am sure there was a reason for the size_t declaration.

If I may ask, what is a size_t variable? I cant seem to find a definition with google, or some other code sites.

As a final piece, I tried to remove the need for the sstream lib, using indirection to get the contents of the string into an int variable, but it didnt work.
Code:
#include<iostream>
#include <string>
#include <sstream>
#include <cctype> //isdigit() and isspace()

using namespace std;

//C++ allows conversion from a floating point to integer. But this function
//doesn't allow that. It wants "strict" (signed) integers only. 
bool string_to_strict_int(const string& sInLine, int& dest);

int main()
{
  string sNumInput;
  int myInt;
  
  //As long as the user doesn't enter a valid, strict (non-floating point)
  //(signed) integer, we keep on asking for one.
  while(string_to_strict_int(sNumInput, myInt) == false )
  {
    cout<<"Please enter an integer"<<endl;
    cout<<"-> : ";
    getline(cin, sNumInput);
  }
  
  //And here we have our (signed) integer at our disposal.
  cout<<myInt<<endl;
  
  cin.get();
  return 0;
}

bool string_to_strict_int(const string& sInLine, int& dest)
{  
  if(sInLine[0] == '-' || isdigit(sInLine[0]) != 0 || isspace(sInLine[0]))
  {
    for(int i = 1 ; i < sInLine.size(); ++i )
    {
      if( !isdigit(sInLine[i] ))
      {
        return false;
      }
    }      
  }
  
  istringstream  InStream( sInLine );
  InStream >> dest;
  if( !InStream )
  {
    //Oops, empty string found. That's not an integer either.
    return false;
  }
  else
  {
    //All went successfull. We had a strict integer value passed to this funct.
    return true;
  }  
}
Feis is offline   Reply With Quote
Old 04-19-2005, 05:07 PM   #5 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
Valmont is on a distinguished road
Quote:
After first running some tests, I realized if you typed a space first, you tricked the entire if statement, as it was only looking for "-" or whether it was a digit. To fix this,...
Fix? This function never intended to "fix" user input. This function wants "strictly" a valid integer. Even a "space-typo" is considered an error and therefore the function didn't process it. Besides, your refactor isn't correct:
what if I enter " -5"? Notice the space before the "-"!

Quote:
I also took cstdlib, and took out size_t for an int. Althoguh I got a warning from the compiler, no errors arised, although I am sure there was a reason for the size_t declaration.

If I may ask, what is a size_t variable? I cant seem to find a definition with google, or some other code sites.
So you replaced definitions yet you don't know what you have replaced...
I'll explain you what that is.

std::size_t is a type that will be converted to the proper unsigned int of your system. The normal int versions differ per unique system: it may be a 16 bit system, 32 bit system, 64 bit system and many more types exist. Think of embedded software for robots.
std::size_t will covert the "unsigned int" into the proper version, making the program only more portable.
So in one sentence:
std::size_t will abstract platform dependent unsigned integer sizes for maximum compatibility.

std::size_t will always be a positive number, which is exactly what we want because indexes can't be negative. All you need to make sure that your array doesn't have 2^31 elements (for a 32-bit system).

Quote:
As a final piece, I tried to remove the need for the sstream lib, using indirection to get the contents of the string into an int variable, but it didnt work.
Because you don't understand the code fully. Google to learn the ways of <sstream>, istringstream and ostringstream.
Once you did that and understand it well, you'll be able to build a function far more efficient then this one. But I left this one as it is so people can play with it. There is a lot to improve.

But you did remove:
Code:
  bool bNonDigit = false;
  bNonDigit = false;
which is good. That was a leftover from my personal library when I modified it a bit.

At least you're thinking. That I appreciate. Study the three things I just told you about, and then let's see what we can do from here .
__________________

Last edited by Valmont; 04-19-2005 at 05:40 PM.
Valmont is offline   Reply With Quote
Old 04-21-2005, 05:50 PM   #6 (permalink)
Feis
Registered User
 
Join Date: Apr 2005
Posts: 18
Feis is on a distinguished road
Ok, after perhaps more careful reflection the function, I think I found a way to simplify the catching of bad input. Basically what this does is that it will _only_ accept input that beigns with "-" or a digit. Otherwise, it reverts to the else statement I wrote in, which returns false. not only is this simpler I think but more foolproof and easier to understand. In retrospect, I dont see why it should have accepted the first character as a space. I'm working on learning sstream, but the midtrimester crush is on Heres the relevant part of the code:


Code:
bool string_to_strict_int(const string& sInLine, int& dest)
{  
  if(sInLine[0] == '-' || isdigit(sInLine[0]) != 0)
  {
    for(size_t i = 1 ; i < sInLine.size(); ++i )
    {
      if( !isdigit(sInLine[i] ))
      {
        return false;
      }
    }
  
  }
  else 
  {return false;}
EDIT: after briefly looking @ istringstream, and a quick test, I found that I could remove the test to see whether the string was null:
Code:
if( !InStream )
  {
    //Oops, empty string found. That's not an integer either.
    return false;
  }
as if the first character was null or a break, the function already returned a false value for it, and there would be no need to check again to see whether it was empty. I remember reading that its always a good idea to check that your stream has data in it(I've dabbled in file input), but I would think that this test has already been performed and therefore the only purpose that code snippet would serve is to make it clear that the stream is being tested. So I ask, what should be done in a situation like this?

Remove redundancy, or make the code more reader friendly?

You could also comment in that the check is already being performed, but a lot is going on in that nest of if and for statements, so I dont think the comments would be effective enough.

Last edited by Feis; 04-21-2005 at 06:33 PM.
Feis is offline   Reply With Quote
Old 04-21-2005, 07:14 PM   #7 (permalink)
357mag
Registered User
 
Join Date: Mar 2005
Posts: 15
357mag is on a distinguished road
Boy you guys are light years ahead of me. Of course I have not made a program in about 5 years and probably will go back to the very beginning of my book. I wish I had you guy's talent.
357mag is offline   Reply With Quote
Old 04-21-2005, 08:15 PM   #8 (permalink)
teknomage1
Jack of all trades
 
teknomage1's Avatar
 
Join Date: Feb 2005
Location: Los Angeles
Posts: 598
teknomage1 is on a distinguished road
Send a message via AIM to teknomage1
'Talent' is merely the ability to look at a problem long enough to fix it. If you take your time, examine the situation and do some research, you can do anything.
__________________
Stop intellectual property from infringing on me
teknomage1 is offline   Reply With Quote
Old 04-22-2005, 12:28 AM   #9 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
Valmont is on a distinguished road
Feis.

I am actually not interested whether such a function catches bad input or not. I am interested if you know what's going on. If you do, you'll be able to make any function you want. I am not stopping you from making a function that accepts "bad input" as well. E.g. " -5 "

Now let's see what presents you brought me.
Code:
if( !InStream )
  {
    //Oops, empty string found. That's not an integer either.
    return false;
  }
This comment is wrong actually. Even misleading. Just like the "bool variable issue"...
Code:
bool bNonDigit = false;
bNonDigit = false;
... you should have removed (or improved) this red comment as well.
Testing for (!InStream ), is the same as (!nstream.good() ).
Why could a stream not be in a good state? Let's see:
- A file could be corrupt.
- A file could be missing.
- A file could be locked.
- The memory contents could be corrupt.
- A hardware failure in general.
- The network doesn't work.
- There is nothing to stream, yet a stream extraction is forced.

This is what happened in my original code:
Code:
istringstream  InStream( sInLine );
InStream >> dest;
if( !InStream )
{
  //Oops, empty string found. That's not an integer either.
  return false;
}
In this very code, two reasons are obvious: no stream is given (an empty string found), or something is wrong with the RAM in the computer. Perhaps more things could be wrong. Use your imagination.
So a more proper comment should have been:
//Stream no good: perhaps empty string passed to this function?

So you missed this one. No problem. We are here to learn.

Let's see, what else do we got?

Quote:
I'm working on learning sstream, ...
The trick is knowing what to learn. I'll teach you 1 minor thing. That's all you need to know on short notice.

std::istringstream : basic in-memory formatting

Let's write some simple code. Three lines, and everybody knows what's going on basically:
Code:
cout<<"Please enter a word. Then press <enter> "<<endl;
string TheWord;
cin>>TheWord;
If you understand this, then you will understand the rest!
Ok, so a std::string variable (=object!) will hold a string. For example the word 'Hello'.
How did this variable got the word in the first place?
1). Well, the word was in memory when the user pressed <enter>.
2) the std::cin operator extracted it from the memory and placed it in "TheWord".
FROM memory TO variable.
Or better:
FROM stream TO variable.
That's all.

Now let's look at stringstreams, especially the std::istringstream stringstream.
This time we need only 1 line of code:
Code:
std::string TheString;
Then somehow, this variable gets a string. We don't care how. I'll give two options:
Code:
std:: string TheString;
std::cin>>TheString; //Option 1!
TheString = "5"; //Option 2!
Whatever, take your pick .

Now we want some advanced formatting done. It reads like it is complicated but not if you realize that we have a special thing to load the string in a stream and then do with it whatever we want! That special thing is called a stringstream. So let's load our string in a stringstream object:
Code:
std:: string TheString;
TheString = "5"; //Option 2!
std::istringstream MyIStream(TheString);
Now we have a stringstream object MyIStream. It holds the unformatted (raw) data of TheString. We can do whatever we want with it. Since it holds a '5' (IT IS NOT A STD::STRING, BUT A STREAM!). That's right, you can use the istringstream object as if it is a normal stream, like you would encounter when pressing <enter> to input some data!
So lets use this stream:
Code:
std:: string TheString;
TheString = "5";
std::istringstream MyIStream(TheString);
int MyInt;
MyIStream >> MyInt; //As if: cin >> MyInt 
Have you read the comment? Just like cin >> MyInt, BUT this time the stream is coming from a stringstream instead of a standard output!
MyInt holds the number 5 now. Once again a few lines of code to show you what they have in common:
Code:
int MyInt;
cin >> MyInt; //User enters data to std stream. Std stream puts it in variable.
string TheString = "5";
istringstream IS(TheString); //Insert data in a different kind of stream.
IS >> MyInt; //Stringstream puts it in a variable.
A Must-Know Feature About (any) Stream
Suppose you do this:
Code:
cin >> MyInt
And the users types first 10 spaces, and then the number and then types <enter>... would this be valid?
The answer is YES!. Why?
Because extractor operators (FROM stream TO variable = EXTRACTION) ignore whitespaces. A space is a whitespace. So suppose a users enters a load of bull after the number he should have entered:
Code:
std::cout<<"Please give me the number 5 man!"<<endl;
int MyFiveInt;
std::cin >> MyFiveInt;
And now the user types this (without the double quotes!) :
Code:
"   5  qwerty  "
Then any stream, like std::cin or std::istringstream will:
1) Ignore the first 3 spaces.
2) Extract 5 to the variable.
3) ignore the following 2 spaces.
4) And leave "qwerty" as trash in the stream buffer.

So here is your major hint!

Now we are going to build up an efficient function to bit by bit:
Make a function that accepts a std::string, and TRIES to convert it into a an int. If it fails to convert then we don't care.
So:
1) " 5 " //good!
2) " -5" //good!
3) " -5 ajfadfa" //good! But trash is left. We don't care.
4) "f5" //fail! but we don't care.
5) " f -5 5" //fail! but we don't care.

Do this first. Keep it simple. We will build up bit by bit!
__________________

Last edited by Valmont; 04-22-2005 at 02:02 AM.
Valmont is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Similar Threads
Thread Thread Starter Forum Replies Last Post
javascript: check if file input is undefined sde HTML, XML, Javascript, AJAX 3 03-28-2005 08:15 AM
Error java.net.ProtocolException: Cannot write output after reading input. wishknew Java 4 03-07-2005 04:01 PM
Database layout - opinions? Rafkin Everything SQL ( MySQL, MSSQL, DB2, Postgre, Oracle, etc...) 4 12-29-2004 07:36 AM
Access denied for user: '@192.168.0.71' (Using password: NO) infinite_root PHP 11 04-28-2004 05:30 PM
dynamic allocation..urgent help needed!!! kashif Standard C, C++ 4 04-21-2003 08:50 AM


All times are GMT -8. The time now is 10:18 AM.


Powered by vBulletin® Version 3.7.0
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.0.0 RC8





Copyright © 2000-2008, Milano Interactive
Web Hosting provided by Portal 360 Web Hosting