How to deal with this side effect? - c++

char sign;
cout << "Enter '+' for addition or '-' for subtraction: ";
cin >> sign;
if ( sign != '+' && sign != '-' ) {
cout << "you can only enter '+' or '-'!\n";
return 1;
}
If I enter +32423, the input is still correct to the cin(it will pass the checking), because + will be automatically set to sign variable, and also 32423 will be stored into my next variable if there is:
cin >> number;
How can I change the code so that +32423 is incorrect to the cin?

You need to decide exactly what the input you require from the user is and what makes it valid. It sounds to me like you want to accept an entire line of input from the user (so you should use std::getline with a std::string) and then you want to only accept strings that are exactly "+" and "-".
std::string sign;
std::getline(std::cin, sign); // Gather input
if (sign != "+" && sign != "-") { // Parse input
// ...
}
// ...
As #H2C03 mentioned, it's a good idea to split up the inputting and the parsing. In this case, the parsing is as simple as comparing the input line to the strings "+" and "-".

Don't try to do two things in one step. This is a common error -- don't feel too bad, the designers of the scanf() family of funcionts and std::istream::operator>> made the exact same mistake.
What you are trying to do: get user input and parse it in order to perform a computation.
What you are actually doing: you are doing these inhrenetly different things in one step, and this is making you confused. You should clearly and cleanly separate the two steps instead: first, get the user input, and the parse it properly. As in:
// get an entire line
std::string line;
std::getline(std::cin, line);
// parse it: only the format <some digits> <'+' or '-'> <other digits>
// is acceptable, most probably with interspersed whitespace
std::string::iterator it = line.begin();
while (isspace(*it) && it != line.end())
it++;
std::string::iterator it2 = it;
while (isdigit(*it) && it != line.end())
it++;
std::string first_number(it2, it); // construct a new substring for the 1st number
// etc. continue the parsing in a similar manner

Related

How to read a complex input with istream&, string& and getline in c++?

I am very new to C++, so I apologize if this isn't a good question but I really need help in understanding how to use istream.
There is a project I have to create where it takes several amounts of input that can be on one line or multiple and then pass it to a vector (this is only part of the project and I would like to try the rest on my own), for example if I were to input this...
>> aaa bb
>> ccccc
>> ddd fff eeeee
Makes a vector of strings with "aaa", "bb", "ccccc", "ddd", "fff", "eeeee"
The input can be a char or string and the program stops asking for input when the return key is hit.
I know getline() gets a line of input and I could probably use a while loop to try and get the input such as...(correct me if I'm wrong)
while(!string.empty())
getline(cin, string);
However, I don't truly understand istream and it doesn't help that my class has not gone over pointers so I don't know how to use istream& or string& and pass it into a vector. On the project description, it said to NOT use stringstream but use functionality from getline(istream&, string&). Can anyone give somewhat of a detailed explanation as to how to make a function using getline(istream&, string&) and then how to use it in the main function?
Any little bit helps!
You're on the right way already; solely, you'd have to pre-fill the string with some dummy to enter the while loop at all. More elegant:
std::string line;
do
{
std::getline(std::cin, line);
}
while(!line.empty());
This should already do the trick reading line by line (but possibly multiple words on one line!) and exiting, if the user enters an empty line (be aware that whitespace followed by newline won't be recognised as such!).
However, if anything on the stream goes wrong, you'll be trapped in an endless loop processing previous input again and again. So best check the stream state as well:
if(!std::getline(std::cin, line))
{
// this is some sample error handling - do whatever you consider appropriate...
std::cerr << "error reading from console" << std::endl;
return -1;
}
As there might be multiple words on a single line, you'd yet have to split them. There are several ways to do so, quite an easy one is using an std::istringstream – you'll discover that it ressembles to what you likely are used to using std::cin:
std::istringstream s(line);
std::string word;
while(s >> word)
{
// append to vector...
}
Be aware that using operator>> ignores leading whitespace and stops after first trailing one (or end of stream, if reached), so you don't have to deal with explicitly.
OK, you're not allowed to use std::stringstream (well, I used std::istringstream, but I suppose this little difference doesn't count, does it?). Changes matter a little, it gets more complex, on the other hand, we can decide ourselves what counts as words an what as separators... We might consider punctuation marks as separators just like whitespace, but allow digits to be part of words, so we'd accept e. g. ab.7c d as "ab", "7c", "d":
auto begin = line.begin();
auto end = begin;
while(end != line.end()) // iterate over each character
{
if(std::isalnum(static_cast<unsigned char>(*end)))
{
// we are inside a word; don't touch begin to remember where
// the word started
++end;
}
else
{
// non-alpha-numeric character!
if(end != begin)
{
// we discovered a word already
// (i. e. we did not move begin together with end)
words.emplace_back(begin, end);
// ('words' being your std::vector<std::string> to place the input into)
}
++end;
begin = end; // skip whatever we had already
}
}
// corner case: a line might end with a word NOT followed by whitespace
// this isn't covered within the loop, so we need to add another check:
if(end != begin)
{
words.emplace_back(begin, end);
}
It shouldn't be too difficult to adjust to different interpretations of what is a separator and what counts as word (e. g. std::isalpha(...) || *end == '_' to detect underscore as part of words, but digits not). There are quite a few helper functions you might find useful...
You could input the value of the first column, then call functions based on the value:
void Process_Value_1(std::istream& input, std::string& value);
void Process_Value_2(std::istream& input, std::string& value);
int main()
{
// ...
std::string first_value;
while (input_file >> first_value)
{
if (first_value == "aaa")
{
Process_Value_1(input_file, first_value);
}
else if (first_value = "ccc")
{
Process_Value_2(input_file, first_value);
}
//...
}
return 0;
}
A sample function could be:
void Process_Value_1(std::istream& input, std::string& value)
{
std::string b;
input >> b;
std::cout << value << "\t" << b << endl;
input.ignore(1000, '\n'); // Ignore until newline.
}
There are other methods to perform the process, such as using tables of function pointers and std::map.

Is there a way to check input data type using only basic concepts?

I'm being challenged to find ways to perform tasks that usually require the use of headers (besides iostream and iomanip) or greater-than-basic C++ knowledge. How can I check the data type of user input using only logical operators, basic arithmetic (+, -, *, /, %), if statements, and while loops?
Obviously the input variable has a declared data type in the first place, but this problem is covering the possibility of the user inputting the wrong data type.
I've tried several methods including the if (!(cin >> var1)) trick, but nothing works correctly. Is this possible at all?
Example
int main() {
int var1, var2;
cin >> var1;
cin >> var2;
cout << var1 << " - " << var2 << " = " << (var1-var2);
return 0;
}
It's possible to input asdf and 5.25 here, so how do I check that the input aren't integers as expected, using only the means I stated earlier?
I understand this problem is vague in many ways, mostly because the restrictions are extremely specific and listing everything I'm allowed to use would be a pain. I guess part of the problem as mentioned in the comments is figuring out how to distinguish between data types in the first place.
You can do that using simple operations, although it might be a little difficult, for example the following function can be used to check if the input is a decimal number. You can extend the idea and check if there is a period in between for floating point numbers.
Add a comment if you need further help.
bool isNumber(char *inp){
int i = 0;
if (inp[0] == '+' || inp[0] == '-') i = 1;
int sign = (inp[0] == '-' ? -1 : 1);
for (; inp[i]; i++){
if (!(inp[i] >= '0' && inp[i] <= '9'))
return false;
}
return true;
}
General checking after reading is done like this:
stream >> variable;
if (not stream.good()) {
// not successful
}
This can be done on any std::ios. It works for standard types (any numeric type, char, string, etc.) stopping at whitespace. If your variable could not be read, good returns false. You can customize it for your own classes (including control over good's return value):
istream & operator>>(istream & stream, YourClass & c)
{
// Read the data from stream into c
return stream;
}
For your specific problem: Suppose you read the characters 42. There is no way of distinguishing between reading it as
- an int
- a double
as both would be perfectly fine. You have to specify the input format more precisely.
The standard library is not magic - you just have to parse the data read from the user, similarly to what the standard library does.
First read the input from the user:
std::string s;
cin >> s;
(you may use getline instead if you want to read a whole line)
Then you can go on parsing it; we'll try to distinguish between integer (*[+-]?[0-9]+ *), real number (*[+-][0-9](\.[0-9]*)?([Ee][+-]?[0-9]+)? *), string (*"[^"]" *) and anything else ("bad").
enum TokenType {
Integer,
Real,
String,
Bad
};
The basic building block is a routine that "eats" consecutive digits; this will help us with the [0-9]* and [0-9]+ parts.
void eatdigits(const char *&rp) {
while(*rp>='0' && *rp<='0') rp++;
}
Also, a routine that skips whitespace can be handy:
void skipws(const char *&rp) {
while(*rp==' ') rp++;
// feel free to skip also tabs and whatever
}
Then we can attack the real problem
TokenType categorize(const char *rp) {
first, we want to skip the whitespace
skipws(rp);
then, we'll try to match the easiest stuff: the string
if(*rp=='"') {
// Skip the string content
while(*rp && *rp!='"') rp++;
// If the string stopped with anything different than " we
// have a parse error
if(!*rp) return Bad;
// Otherwise, skip the trailing whitespace
skipws(rp);
// And check if we got at the end
return *rp?Bad:String;
}
Then, on to numbers, notice that the real and integer definitions start in the same way; we have a common branch:
// If there's a + or -, it's fine, skip it
if(*rp=='+' || *rp=='-') rp++;
const char *before=rp;
// Skip the digits
eatdigits(rp);
// If we didn't manage to find any digit, it's not a valid number
if(rp==start) return Bad;
// If it ends here or after whitespace, it's an integer
if(!*rp) return Integer;
before = rp;
skipws(rp);
if(before!=rp) return *rp?Bad:Integer;
If we notice that there's still stuff, we tackle the real number:
// Maybe something after the decimal dot?
if(*rp=='.') {
rp++;
eatdigits(rp);
}
// Exponent
if(*rp=='E' || *rp=='e') {
rp++;
if(*rp=='+' || *rp=='-') rp++;
before=rp;
eatdigits(rp);
if(before==rp) return Bad;
}
skipws(rp);
return *rp?Bad:Real;
}
You can easily invoke this routine after reading the input.
(notice that here the string thing is just for fun, cin does not have any special processing for double-quotes delimited strings).

split string - multiple delimiter C++

so this user input all in one line string , i need to parse the input into two categories: user age and user name.
for example , the user input -->> [23:Frank] [15:Jack] [45:] [33:sofia] []
in this case i have more than one argument (delimiter, total of 3 ) which are [:] , in addition i need to get user input and stop looping once i encounter the [] at the end.
this is what i was thinking :
string input;
vector<string> age;
vector<string> name;
cin >> input;
while (input != "[]")
{
get age between first [ and :
assign to age variable
get name between : ]
assign to user name variable
................
}
also - what if one of the brackets is missing a name , how can assign a blank name and skip that part in order to process the rest (meaning i will output age with no name next to it).
any suggestions regarding how to get and process the data.
i saw some advance stuff like Toknizing and booster which are advance for my course, that's why i was thinking about straight forward getline and parse functions.
Thank you.
Read in token like you are currently doing with cin
test for [] as you are doing with the while loop
For the inside the loop, here are a few things to help you out:
std::string's front and back functions are perfect for ensuring that input starts with [ and ends with ]
std::string's substr function is perfect for trimming off the [] so you can easily ignore them for the rest of the parsing
std::stringstream allows you to call make a stream that only contains your trimmed input.
std::getline(stream, string, char) will read all characters it finds up to the char parameter or the end of the stream and stuff the results in the string parameter and then discard the char it found so you won't trip over it parsing the rest of the input.
strtoul will convert a string into a number and tell you if it failed. It will not accept negative numbers so you can catch people trying to trick your program.
getline(stream, string) will read the stream until it hits an end of line marker. Perfect for reading the rest of a stream that contains no end of lines.
Using strtoul:
char * endp;
unsigned long agenum strtoul(agestr.c_str(), // turn string into old c-style string
&endp, // will be updated with the end of the char after the last number
10); // base ten numbers
if (endp != '\0') // old c-strings always end on a null (numerical zero). If strtoul
// didn't end on a null, the string wasn't a valid number.
{
//not a number
}
Ok , so THANKS for people who helped or at least tried to help!!
what i end up doing for this part was as follows:
read in each string at once
use the find function in order to locate my delimiters ( which are in this case [ : ] )
return positions per each perimeter based on my argument (each pair will hold either the beginning and the end for age || name)
pass those arguments results to truncate the string by using substr function, then assign into each variables.
while (true)
{
string myInput;
cin >> myInput;
while (myInput != "[]")
{
int age_beg = myInput.find('[') + 1 ;
int age_end = myInput.find(':', age_beg);
string age = myInput.substr(age_beg, (age_end - age_beg));
int name_beg = age_end + 1;
int name_end = myInput.find(']', name_beg);
string name = myInput.substr(name_beg, (name_end - name_beg));
cout << "NAME : " << name << " AGE : " << age << endl;
cin >> myInput;
}
}
Hope this will help others with the same question in the future !!

Simple C++ not reading EOF

I'm having a hard time understanding why while (cin.get(Ch)) doesn't see the EOF. I read in a text file with 3 words, and when I debug my WordCount is at 3 (just what I hoped for). Then it goes back to the while loop and gets stuck. Ch then has no value. I thought that after the newline it would read the EOF and break out. I am not allowed to use <fstream>, I have to use redirection in DOS. Thank you so much.
#include <iostream>
using namespace std;
int main()
{
char Ch = ' ';
int WordCount = 0;
int LetterCount = 0;
cout << "(Reading file...)" << endl;
while (cin.get(Ch))
{
if ((Ch == '\n') || (Ch == ' '))
{
++WordCount;
LetterCount = 0;
}
else
++LetterCount;
}
cout << "Number of words => " << WordCount << endl;
return 0;
}
while (cin >> Ch)
{ // we get in here if, and only if, the >> was successful
if ((Ch == '\n') || (Ch == ' '))
{
++WordCount;
LetterCount = 0;
}
else
++LetterCount;
}
That's the safe, and common, way to rewrite your code safely and with minimal changes.
(Your code is unusual, trying to scan all characters and count whitespace and newlines. I'll give a more general answer to a slightly different question - how to read in all the words.)
The safest way to check if a stream is finished if if(stream). Beware of if(stream.good()) - it doesn't always work as expected and will sometimes quit too early. The last >> into a char will not take us to EOF, but the last >> into an int or string will take us to EOF. This inconsistency can be confusing. Therefore, it is not correct to use good(), or any other test that tests EOF.
string word;
while(cin >> word) {
++word_count;
}
There is an important difference between if(cin) and if(cin.good()). The former is the operator bool conversion. Usually, in this context, you want to test:
"did the last extraction operation succeed or fail?"
This is not the same as:
"are we now at EOF?"
After the last word has been read by cin >> word, the string is at EOF. But the word is still valid and contains the last word.
TLDR: The eof bit is not important. The bad bit is. This tells us that the last extraction was a failure.
The Counting
The program counts newline and space characters as words. In your file contents "this if fun!" I see two spaces and no newline. This is consistent with the observed output indicating two words.
Have you tried looking at your file with a hex editor or something similar to be sure of the exact contents?
You could also change your program to count one more word if the last character read in the loop was a letter. This way you don't have to have newline terminated input files.
Loop Termination
I have no explanation for your loop termination issues. The while-condition looks fine to me. istream::get(char&) returns a stream reference. In a while-condition, depending on the C++ level your compiler implements, operator bool or operator void* will be applied to the reference to indicate if further reading is possible.
Idiom
The standard idiom for reading from a stream is
char c = 0;
while( cin >> c )
process(c);
I do not deviate from it without serious reason.
you input file is
this is fun!{EOF}
two spaces make WordCount increase to 2
and then EOF, exit loop! if you add a new line, you input file is
this is fun!\n{EOF}
I took your program loaded it in to visual studio 2013, changed cin to an fstream object that opened a file called stuff.txt which contains the exact characters "This is fun!/n/r" and the program worked. As previous answers have indicated, be careful because if there's not a /n at the end of the text the program will miss the last word. However, I wasn't able to replicate the application hanging in an infinite loop. The code as written looks correct to me.
cin.get(char) returns a reference to an istream object which then has it's operator bool() called which returns false when any of the error bits are set. There are some better ways to write this code to deal with other error conditions... but this code works for me.
In your case, the correct way to bail out of the loop is:
while (cin.good()) {
char Ch = cin.get();
if (cin.good()) {
// do something with Ch
}
}
That said, there are probably better ways to do what you're trying to do.

Try to use in cin,peek()

I try to get from the user inputs till blank line
so I wrote this:
while (c != '\n')
{
c = cin.peek();
cin >> check;
if (check == "Test")
{
cin >> ID >> One >> Two >> Three;
Test[i++] = Test(ID, One, Two, Three);
}
}
to example, I get from the user Test 12 45 56 78 99 now, check=test, id=45, one, 56, two=78, three=99
and when I enter empty line, why the while loop isn't stopped?
It's not clear what you're trying to do; you don't use the
results of cin.peek() until far later, after having done
significantly more input, without testing whether it succeeded
or not. Given the code, my first question is: do you understand
how while works? Modifying the value of a control variable
within the loop will not cause you to break out of the loop; the
test is only done at the top of the loop. And you must always
verify that input has succeeded before using the variables
you've input.
If your input is line oriented, the classical solution would
be:
std::string line;
while ( std::getline( std::cin, line ) && !isEmpty( line ) ) {
std::istringstream parser( line );
if ( parser >> check >> ID >> One >> Two >> Three >> std::ws
&& parser.get() == EOF ) {
// Data is good, can be used...
} else {
// Some sort of format error in the line...
}
}
I've put the test for an empty line in a separate function,
because you probably want to treat a line with just white space
as empty. (Users may accidentally hit the space bar before
enter, and what they see will still look like an empty line.)
That's also why I >> into std::ws before checking that
there's no garbage at the end of line when parsing.
cin >> check;
is a formatted input function, meaning it will discard leading white space. A blank line is just leading white space, it will be discarded and the extraction operator will keep reading until non-whitespace data arrives, or an I/O error occurs.