Why is my input so messed up in this C++ program? - c++

I am trying to make a command line mail application for Linux (though I'm developing on Xcode cause a VM uses a lot of power for my computer). I have two options in the main menu (where the user inputs a number to select). I use this after every input whether a simple can or a get line(can, stringName):
void clearCin() {
cin.clear();
cin.ignore(INT_MAX, '\n');
}
That is why I am perplexed that I am getting all sorts of weird behavior with my input. Here's my code and a sample output (the end function is just a for loop that does cost << endl;):
Code
bool stdEmail() {
string to, cc, bcc, subject, message;
cout << "When entering email addresses, seperate multiple email addresses with a space";
endl(2);
cout << "To: ";
getline(cin, to);
clearCin();
cout << "cc: ";
getline(cin, cc);
clearCin();
cout << "bcc: ";
getline(cin, bcc);
clearCin();
endl(1);
cout << "Subject: ";
getline(cin, subject);
clearCin();
endl(1);
cout << "Now enter your message, when you're finished, type a period on a new line";
endl(2);
ofstream file;
file.open("newMessage.txt", fstream::trunc);
bool repeat = true;
while (repeat) {
getline(cin, message);
clearCin();
if (message == ".") {
repeat = false;
} else {
file << message << endl;
}
}
file.close();
return true;
}
Output
--------------------------------------------------
Welcome to mark's Multi-Mail program
Main menu:
--------------------------------------------------
1. Send personalized emails to multiple recipients
2. Send a standard email
3. Exit the program
--------------------------------------------------
2
When entering email addresses, seperate multiple email addresses with a space
To: one#example.com two#example.com
cc: three#example.com
bcc:
Subject: Thanks for your help!
Now enter your message, when you're finished, type a period on a new line
Here is a sample message
.
Now I am trying to get it out of taking the message which should have happened when I typed a .
.
Program ending Have a Nice Day
Program ended with exit code: 0
Here is what shows up in newMessage.txt:
Here is a sample message

cin.ignore(INT_MAX, '\n'); discards a newline and getline completes when it reads a newline so you would have to press enter twice after typing your message for getline to read it and you would have to press enter twice after entering a '.' to end your loop.

Related

validate string input with while loop

I wanted to validate a string that will be inputted by the user which follows two conditions. The condition would be whether the string is empty or the string has a space char. My current problem is that I can validate the string but it would require me to press enter once more time to reiterate my question "2. Enter Product Name: ".
while (true) {
cout << "2. Enter Product Name: ";
if(getline(cin, newNode->product_name)) {
if ((newNode->product_name).empty() || (newNode->product_name) == " ") {
cout << "Please enter a valid product name!\n";
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
}
else {
break;
}
}
}
Being inside the if statement if(getline(cin, newNode->product_name)) { means that the reading of a line succeeded. Therefore, you don't need the lines
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
They will request an extra line to ignore, so remove that lines.

C++: Using end of file (ctrl+z) to end a loop seems to break cin for the rest of my program

The rest of the program still runs, but none of the cin's prompt the user for input.
double sales;
cout << "(Commision Employee) Please enter the employee's Sales: ";
while (cin >> sales) {
try
{
employee1.setGrossSales(sales);
}
catch (invalid_argument& e)
{
cout << "\nException: " << e.what() << "\n\n";
}
cout << "Please re-enter value if an error occured, otherwise enter end-of-file (ctrl+z): ";
}
string ssn;
cout << "(Base Plus Commision Employee) Please enter the SSN with dashes: ";
cin >> ssn;
employee2.setSocialSecurityNumber(ssn);
The cin following the loop doesn't function like I expected it too. What I gather is that cin has a boolean end-of-file marker that is switched to true upon receiving the end-of-file (ctrl+z on windows). It's this marker that prevents cin from operating again. I have found that I can use std::cin.clear(); to clear this marker.
I found this answer here which does a better job at both describing the same issue I had and answering it: while (cin >> x) and end-of-file issues
For my purposes, this was all I needed.
ctrl+z is actually 0x1A byte that sets the std::basic_ios::eof to true, which means that associated stream has reached end-of-file.
Windows system can not read beyond the 0x1A (EOF) character but Unix can.

Catch cin exception

I want to ask the user for input, which I get with cin like this
void AskForGroundstate() {
cout << "Please enter an groundstate potential value in Volt:" << endl;
if (!(cin >> _VGroundstate)) {
cin.clear();
cin.ignore();
cout << "Groundstate potential not valid." << endl;
AskForGroundstate();
}
}
_VGroundstate is a double, so if the user enters an String with not numbers, it should ask him again for a better input. But the problem is, that when the input is for example "AA", than the program executes AskForGroundstate two times, with "AAA" three times etc. Did I use the clear wrong?
The problem is that cin.ignore() drops one character; you want to drop all characters to end of line:
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
This ensures that all invalid input is dropped before end-users are prompted for input again.

Cin is not working with array of chars

When I try to get from user a username, I make the following:
#include <iostream>
using namespace std;
void main(){
char *usrn=new char[20]; //Max username length of 20 alfanumeric characters
std::string usrn_str;
while (true){
std::cout << "Enter the username(3-20 characters): ";
cin.clear();
cin.ignore();
std::cin.getline(usrn,22);
usrn_str=usrn;
if ((usrn_str.length())<3){
cout << "Introduced username too short!" << endl;
}
else if ((usrn_str.length())>=21){
cout << "Introduced username too long!" << endl;
}
else {
cout << usrn_str.c_str() ;
}
}
}
Anyway, when introducing a larger username than the allowed one, i.e 25, it shows me the message that the introduced username is too long, but in the next loop, I can't enter again the username, because it takes as I've entered the last 5 characters in the mentioned example. Summing up, if I enter a 30 length username, it discards the first 20 and sets the last 10 ones as the username, when I want to be asking the username till I get a 3-20 length username.
How could I implement it? Any help is appreciated.
Use std::getline() to read the whole user input (user input is line based). Then do the validation checkes against the input line.
bool finished = false;
std::string name;
do
{
if (std::getline(std::cin, name))
{
// You have successfully read one line of user input.
// User input is line based so this is usually the answer to
// one question.
//
// Do your validation checks here.
// If the user entered data that checks out then set
// finished to true.
}
else
{
// There was a problem reading the line.
// You need to reset the stream to a good state
// before proceeding or exit the application.
}
}
while(!finished);

Stopping the user from entering letters? C++

Hi i'm newish to C++ but i have a little problem which is i have to stop the user entering letters in a number section. I have made an attempt which works but its dodgy, because it will allow the user to continue then will tell them they have got something wrong and to restart the application. How do i validate it to bring up an error message telling them thats not a number and let them re enter a number?
Here is the code:
double Rheight;
do
{
cout << "Enter height of the room. " << endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 4);
cout << "WARNING: If you enter a letter the program will exit." << endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
cin >> Rheight;
}
while (Rheight > 20 || Rheight == 0);
Ask if you need to see more code.
There are basically two components to the answer:
Detecting that the input failed.
Cleaning up after a failed input.
The first part is rather trivial: you should always test after input that the stream is in a good state before using the input. For example:
if (std::cin >> value) {
// use value
}
else {
// deal with the input error
}
How to deal with the input error depends on your needs. When reading a file you'd probably abort reading the entire file. When reading from standard input you can ignore just the next character, the entire line, etc. Most like you'd want to ignore the entire line. Before doing so you'll need to put the stream back into a good state:
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
The first line clears the stream's error flags and the second line is a magic incantation ignoring as many characters as necessary until a newline got ignored.
To check if the input was valid you can use
if(!(cin >> Rheight))
{
cout << "Please input a valid number!" << endl;
continue;
}