std::getline not re-requesting user input in while loop [duplicate] - c++

This question already has answers here:
Why does std::getline() skip input after a formatted extraction?
(5 answers)
Closed 2 years ago.
I am trying to write a program where a user is asked for input and then I check if the 2 sub strings (splitting in half) is the mirror image. I want to loop continually until a user stops entering in input but my code is not looping correctly. The code works fine on the first iteration of the while loop, but then during the second iteration of the while loop, getline() does not re-request user input, it just sets std::string input to "".
I'm sure I am missing something obvious.
Code
#include <iostream>
#include <string>
#include <stack>
#include <algorithm>
using namespace std;
bool compareMirrorString(const std::string&, const std::string&);
int main()
{
std::string input;
string first_string;
string second_string;
bool result;
int input_length;
int first_string_length;
std::string quit;
cout << "Enter 2 strings to compare, seperated by a # e.g. \"abc#cba\"." << endl;
while ( true )
{
std::getline(std::cin, input);
//split string into its two component parts
input_length = input.length();
first_string_length = input_length / 2;
first_string.assign(input, 0, first_string_length);
second_string.assign(input, first_string_length + 1);
//test if two strings are mirror images of each other
result = compareMirrorString(first_string, second_string);
if (result) {
cout << "Yes they match.";
}
else {
cout << "No they do not match.";
}
cout << "\nDo you want to test another string? Y for yes, q to quit." << endl;
cin >> quit;
if (quit == "q" or quit == "Q" or quit == "quit" or quit == "Quit" or quit == "QUIT"
or quit == "no" or quit == "No")
{
break;
}
else
{
cout << "Enter another 2 strings to compare, seperated by a # e.g. \"abc#cba\"." << endl;
}
} //end of while
return 0;
}
//is second a mirror image of first?
bool compareMirrorString(const std::string& first, const std::string& second)
{
if (first.length() != second.length()) {
return false;
}
//put first_string on stack
std::stack<char> stackChar;
for (auto elem : first){
stackChar.push(elem);
}
int size = stackChar.size();
//compare first and second strings
bool compare_equal = true;
for (int i = 0; i < size; i++)
{
if (stackChar.top() == second[i])
{
stackChar.pop();
}
else
{
compare_equal = false;
break;
}
}
return compare_equal;
}

In the first iteration of the loop:
cin >> quit;
will read up to, but not including the newline character.
In the second iteration of the loop:
std::getline(std::cin, input);
will read that newline character, and hence read an empty string into input.
There are several ways to fix this
You could use getline to read the string quit
You could read input again, if it happens to be an empty string.
You could call cin.get(); after the cin >> quit to clear the newline.

Related

How to read cin into a vector

I need to allow the user to enter a writing sample in the console or as a file and have my program split that input into a word vector (one word per item of vector). This is my current code:
while(cin >> inputString) {
wordVector.push_back(inputString);
}
The trouble is, when I run this, it works fine until it reaches the end of the user's input. Then it seems to just endlessly loop.
inputString is type string.
wordVector is type string.
This is the full code: (the broken code is at the bottom)
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
// Debug message flag
const bool DEBUG = false;
// Prototypes
void splitToVectors(vector<string>&,vector<string>&,vector<int>&,int &);
double avSentLength(const vector<string>);
double avWordSyl(const vector<string>,const vector<char>);
double percentSentLong(const vector<int>,int);
int numSyllables(const vector<char>);
void nextScreen(int);
int main() {
// Initialize variables and vectors
bool validate;
int characters,words,sentences = 0,syllables;
string file;
string inputString;
char inputChar;
int input;
vector<string> wordVector;
vector<char> charVector;
vector<string> sentenceVector;
vector<int> numWordsInSent;
// Get writing sample
do {
// Request preferred location
validate = true;
cout << "Would you like to:" << endl;
cout << " 1. Enter the writing sample in the console" << endl;
cout << " 2. Read from a file" << endl << " > ";
// Validate
if(!(cin >> input)) { // This error checking condition functions as the cin
validate = false;
cin.clear();
cin.ignore(100, '\n');
}
if((input < 1) || (input > 2)) {
validate = false;
}
} while(!validate);
// Transfer selected source to wordVector
if(input == 1) {
// Request sample
cout << "Please enter the writing sample below:" << endl << endl;
// Input sample
while(cin >> inputString) {
wordVector.push_back(inputString);
}
}
}
I have not yet learned about iterators. So I came up with the following solution:
I use a getline to take all input and place into a string variable. I then have a for loop run through it, building a temporary string as it goes, until it encounters a space. When it sees a space, it adds the temporary variable to the vector, and resets the temporary variable. It continues this way until it reaches the end of the string.
Are you sure you are hitting Ctrl-D to send the EOF properly? The following code seems to work:
int main()
{
vector<string> words;
std::string inputString;
while (cin >> inputString)
{
words.push_back(inputString);
}
vector<string>::iterator it;
for (it = words.begin(); it != words.end(); it++)
{
cout << *it << "\n";
}
return 0;
}
In an interactive console/compiler while(cin >> inputString) will continue wait for user-input.
It may work on a non-interactive console/compiler that reads data from a static standard input. But it's worth noting that in (most conforming) interactive compilers, cin >> inputString will continue to wait for user input, and will (should) not evaluate to false until there occurs an error in reading input.
You may want to signal the program that input is finished. One way of doing this is to provide a keyword such as EOF which will break the while-loop (although the disadvantage of this is that you can't use EOF in the content of your input).

Why does this while loop output the same thing no matter what you input?

I have a program that does three things. Asks you how many variables you wan't, ask you to input each variable, then stores it in a vector. I have put some code that checks if your input is correct, and if it isn't, re-loops the code asking for your variable. The problem I am having is that when you type anything in around the second variable, it asks you to try again infinitely.
For instance, if I typed these values into the input:
Variable amount: 5
Please input variable 1: 8
Please input variable 2: 8
ERROR, PLEASE ENTER ONLY VALID SYMBOLS
---------------------
Please input variable 2:
It would keep outputting ERROR, PLEASE ENTER ONLY VALID SYMBOLS over and over again no matter what you typed. The code is down below, and if you have a better name for this question please let me know. (I'm not really sure what to call this)
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int inputErrorMessage()
{
cout << "\n ERROR, PLEASE ENTER ONLY VALID SYMBOLS \n";
cout << "--------------------- \n";
return 0;
}
int main()
{
// Declare the variables, vectors, etc.
int varNum = 1;
int totVar = 0;
int choice = 0;
vector<int> userNums;
double input = 0;
string checktotVar = "";
string checkInput = "";
string sym = "";
bool valid = false;
stringstream sstotVar;
stringstream ssinput;
if (choice != 6) {
while (!valid) {
valid = true;
// Ask user for how many variables they want then record it
cout << "Variable amount: ";
getline(cin, checktotVar);
sstotVar << checktotVar;
sstotVar >> totVar;
if (sstotVar.fail() || totVar <= 0) {
inputErrorMessage();
valid = false;
sstotVar.clear();
sstotVar.ignore();
}
}
valid = false;
while (!valid) {
valid = true;
// Ask the user for each variable, then record it into the array
for (int i = 0; i < totVar; ++i) {
cout << "Please input variable " << varNum << ": ";
getline(cin, checkInput);
ssinput << checkInput;
ssinput >> input;
if (ssinput.fail()) {
inputErrorMessage();
valid = false;
ssinput.clear();
ssinput.ignore();
}
if (valid == true) {
userNums.push_back(input);
varNum++;
}
}
}
}
}
ssinput >> input;
reads the one thing in ssinput right to the end of the stream while leaving the read valid. The next time around
ssinput << checkInput;
can't write into the stream because the stream hit the stream's end. That means the read also fails and
if (ssinput.fail()) {
enters the body of the if where the program clears the error
ssinput.clear();
and then promptly reads off the end of the stream with
ssinput.ignore();
causing the error all over again.
Quickest solution:
Recreate
stringstream ssinput;
on each loop iteration. So
stringstream sstotVar;
//stringstream ssinput; gone from here
and
getline(cin, checkInput);
stringstream ssinput(checkInput); // and now tighter scope recreated each loop.
ssinput >> input;
Also by keeping the stream around without emptying it out it can get very., very big.
You can also simplify your logic around
while (!valid) {
and eliminate some repeated code by moving the read validation into it's own function
int getMeANumber(const std::string & message, int min)
that loops until it gets a number and then returns that number. For example:
int getMeANumber(const std::string & message, int min)
{
while (true)
{
cout << message;
string checktotVar;
getline(cin, checktotVar);
stringstream sstotVar(checktotVar);
int totVar;
sstotVar >> totVar;
if (!sstotVar || totVar <= min)
{
inputErrorMessage();
}
else
{
return totVar;
}
}
}
Now main is this itty-bitty tiny lil' thing.
int main()
{
int choice = 0;
vector<int> userNums;
if (choice != 6)
{
int totVar = getMeANumber("Variable amount: ", 0);
for (int i = 0; i < totVar; ++i)
{
stringstream varname;
varname << "Please input variable " << i+1 << ": ";
userNums.push_back(getMeANumber(varname.str(), numeric_limits<int>::min()));
// numeric_limits<int>::min requires #include <limits>
}
}
}
Here are the issues with this code.
In this part:
if (valid == true) {
userNums.push_back(input);
varNum++;
}
you forgot to add an ssinput.clear(). This will reset the stream state (clear the error flags), otherwise you cannot use it again. That is why it stops working at the second input.
In addition, even though this works, you are pushing back a variable that you declared as double into a vector of ints. That is bound to cause issues if this was intended to store double variables, instead of truncating them and storing them as ints.
It should be:
#include <iostream>
#include <cmath>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
using namespace std;
int inputErrorMessage()
{
cout << "\n ERROR, PLEASE ENTER ONLY VALID SYMBOLS \n";
cout << "--------------------- \n";
return 0;
}
int main()
{
// Declare the variables, vectors, etc.
int varNum = 1;
int totVar = 0;
int choice = 0;
vector<int> userNums;
double input = 0;
string checktotVar = "";
string checkInput = "";
string sym = "";
bool valid = false;
stringstream sstotVar;
stringstream ssinput;
if (choice != 6) {
while (!valid) {
valid = true;
// Ask user for how many variables they want then record it
cout << "Variable amount: ";
getline(cin, checktotVar);
sstotVar << checktotVar;
sstotVar >> totVar;
if (sstotVar.fail() || totVar <= 0) {
inputErrorMessage();
valid = false;
sstotVar.clear();
sstotVar.ignore();
}
}
valid = false;
while (!valid) {
valid = true;
// Ask the user for each variable, then record it into the array
for (int i = 0; i < totVar; ++i) {
cout << "Please input variable " << varNum << ": ";
getline(cin, checkInput);
ssinput << checkInput;
ssinput >> input;
if (ssinput.fail()) {
inputErrorMessage();
valid = false;
}
if (valid == true) {
userNums.push_back(input);
varNum++;
}
ssinput.clear();
}
}
}
}
EDIT: You need to clear the stringstream on each iteration of the loop, otherwise you're not writing to an empty stream when you grab the next input from the user, which is what's causing the .fail() method to return true after the first iteration of the loop.

Why is a space in the string making my code loop infinitely? [duplicate]

This question already has answers here:
Infinite loop with cin when typing string while a number is expected
(4 answers)
Closed 6 years ago.
I have the following code which simply takes a string and find each character's index in the alphabet.
void encrypt()
{
string alpha = "abcdefghijklmnopqrstuvwxyz";
string word;
vector<char> temp;
char a, b;
cout << "Enter string to encrypt: \n";
cin >> word;
for (int i=0; i<word.length(); i++)
{
bool t = false;
a = word[i];
for (int j=0; j<alpha.length(); j++)
{
b = alpha[j];
if (a == b)
{
cout << a << "'s index = " << j+1 << endl;
t = true;
}
}
if (t == false)
{
cout << "space here\n";
}
}
}
when i input a word/string with no space the code works fine but when i input a string with a space the program goes into an infinite loop.
edit main() added due to request:
main()
{
int a;
bool b = false;
while (b == false)
{
cout << "1. Encrypt a string\n";
cout << "2. Decrypt a string\n";
cout << "3. Exit\n";
cout << endl;
cin >> a;
cout << endl;
if (a == 1)
{
encrypt();
}
else if (a == 2)
{
decrypt();
}
else if (a == 3)
{
b = true;
}
}
return 0;
}
cin >> word;
will read only the first word and leave the second word in the input stream. After that, the call
cin >> a;
will result in an error unless the second word starts with a number. Once the program enters a state of error, nothing is read and the program stays in a loop.
To diagnose problems like these, always check the state of the stream after a read operation.
if ( cin >> word )
{
// Use word
}
else
{
// Deal with error.
}
if ( cin >> a )
{
// Use a
}
else
{
// Deal with error.
}
To address your real problem, don't use operator>> to read space separated string. Use getline (and use a variable name different from word).
std::string str;
if ( getline(std::cin, str) )
{
// Use str
}
else
{
// Deal with error.
}
However, in order to use getline successfully, you have to make sure that after a is read, you ignore the rest of the line. Otherwise, the rest of the line will be read by getline.
if ( cin >> a )
{
// Ignore rest of the line
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// Use a
}
else
{
// Deal with error.
}
Replace cin >> word; with getline(cin, word);. It will accept a line as input. Which will resolves your input containing spaces.
As far as infinite loop concern, clear the error bits on the stream cin.clear();
You can check whether cin is accepting the space separated string completely, by doing a cout instantly after the cin. If cin is not accepting the space separated string, then try using getline
Issue resolved:
Use the following:
cout << "Enter string to encrypt: ";
scanf(" %[^\n]s",word);
for (int i=0; word[i]!='\0'; i++)
{
use
include <cstdio>
Hope this solves the problem!! I will get back to you with the solution using string..

Searching text file for specific user-inputted string

I'm trying to write a program which opens a text file full of words (a "dictionary" minus the definitions) and stores these values in strings to compare them against a user input to determine whether the user input is spelled correctly.
I go the program to work and do what I wanted, but I can't seem to figure out one specific detail. I want the program to continue running until the user enters "exit" as an input. The only problem is that my program continues spewing out either "input is spelled correctly" or "input is not spelled correctly" ad infinitum without giving the user a chance to input more values in.
How do I make it so the program only outputs one of these two options only once and then prompts the user for another input instead of a never-ending stream of the same statement? Thank you in advanced!
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string line; //holds values from txt file
string input; //holds user-inputted values
ifstream inputFile; //fstream operator declaration
bool isFound = false; //bool value to indicate if the string has been found
inputFile.open("dict.txt", ios::in);
if (inputFile)
{
cout << "Enter word to spellcheck (or exit to end)\n";
getline(cin, input);
while (input != "exit")
{
while (getline(inputFile, line))
{
if (input == line)
{
isFound = true;
break;
}
else
{
isFound = false;
}
}
inputFile.close();
if (isFound)
{
cout << input << " is spelled correctly.\n";
}
else
{
cout << input << " is not spelled correctly.\n";
}
}
if (input == "exit")
{
cout << "Ending program...\n";
}
}
else
{
cout << "Cannot open file\n";
}
return 0;
}
Inside the body of the
while (input != "exit")
loop the user is never asked to update the value of input. Moving getline(cin, input) into the while condition like this:
while (getline(cin, input) && input != "exit")
will solve that problem.
Then next problem is the handling of the dictionary file. It is closed in the middle of the loop, so subsequent reads from it will instantly fail. OP could reset the read pointer to the beginning of the file with inputFile.seekg(0);, but why reread the file every time.
Instead read the dictionary file into a std::set with more or less the same code as used in the search:
std::set<std::string> dictionary;
while (getline(inputFile, line))
{
dictionary.insert(line);
}
at the beginning of the program and search the set for the user's input in the loop.
if (dictionary.find(input) != dictionary.end())
{
cout << input << " is spelled correctly.\n";
}
else
{
cout << input << " is not spelled correctly.\n";
}
This should do the trick, you just need to move your getline block in the while loop, and move the file close statement outside the while loop:
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
using namespace std;
int main()
{
string line; //holds values from txt file
string input; //holds user-inputted values
ifstream inputFile; //fstream operator declaration
bool isFound = false; //bool value to indicate if the string has been found
inputFile.open("dict.txt", ios::in);
if (inputFile)
{
while (input != "exit")
{
// Rewind file back to beginning every time
inputFile.clear();
inputFile.seekg(0,std::ios::beg);
cout << "Enter word to spellcheck (or exit to end)\n";
getline(cin, input);
while (getline(inputFile, line))
{
if (input == line)
{
isFound = true;
break;
}
else
{
isFound = false;
}
}
if (isFound )
{
cout << input << " is spelled correctly.\n";
}
else
{
if (input != "exit"){ // Don't print message if exiting
cout << input << " is not spelled correctly.\n";
}
}
}
if (input == "exit")
{
cout << "Ending program...\n";
}
inputFile.close();
}
else
{
cout << "Cannot open file\n";
}
return 0;
}

Why does this not ask user for another input?

This part of a larger project. Right now it's supposed to ask user for a string, calculate how many words are in it, print out the # of words, ask user if they want to do it again, then if they want to, ask for another string, and so on. But this only works fine the first time. After that, it takes the answer to the yes/no question as the test string. For example: I like coding. 3. Again? Yes/no. Yes. 1. Again? Yes/no... Can someone tell me how to fix this glitch?
#include <iostream>
#include <string>
using namespace std;
string original[10] = { "hello", "sir", "madam", "officer", "stranger", "where", "is", "the", "my", "your" };
string translated[10] = { "ahoy", "matey", "proud beauty", "foul blaggart", "scurvy dog", "whar", "be", "th'", "me", "yer" };
string input;
string ans;
bool playAgain()
{
cout << "Another? yes/no: ";
cin >> ans;
if (ans.compare("yes") == 0) { return true; }
if (ans.compare("no") == 0) { return false; }
}
int getNumOfWords(string input)
{
int numOfSpaces = 0;
string current;
for (int i = 0; i < input.length(); i++)
{
current = input.at(i);
if (current.compare(" ") == 0)
{
numOfSpaces++;
}
}
return numOfSpaces + 1;
}
void play(string input)
{
int numOfWords = getNumOfWords(input);
cout << numOfWords << endl;
}
void start()
{
getline(cin, input);
play(input);
}
int main()
{
bool playing;
do
{
start();
playing = playAgain();
} while (playing);
return 0;
}
When cin.getline() reads from the input, there is a newline character left in the input stream, so it doesn't read your c-string. Use cin.ignore() beore calling getline()
void start()
{ cin.ignore();
getline(cin, input);
play(input);
}
It's because of the difference between getline and cout. The former reads in the entire line up to and including the terminating \n, while cout will read only up to the \n or whitespace. The cin in your code reads in yes or no to ans (try printing it out immediately afterwards), but it doesn't account for the \n. Thus, when you call getline it finds the \n waiting in stdin, and so reads that into input instead of blocking until cin wasn't empty.