Stop input loop when input is done | std::cin - c++

I want to write a function that gets a set of integers and saves them to a vector.
To get the integers I'm using a while loop.
Enter the vector elements: 1 2 3 4 5
I want the loop to stop looking for input after the last element is inputted or until a non-numberis inputted, however I'm having trouble with it.
It is an assignment, so it needs to read input from std::cin.
This is my code:
#include <iostream>
#include <vector>
bool NumInVect(int num, std::vector<int> vect)
{
bool numInVect = false;
for (int i = 0; i < vect.size(); i++)
{
if (vect[i] == num)
{
numInVect = true;
}
}
return numInVect;
}
int main() {
std::vector<int> tempVect;
std::vector<int> finalVector;
std::cout << "Enter the vector elements: ";
int currVal;
while (std::cin >> currVal)
{
std::cout << "-\n";
if (!NumInVect(currVal, tempVect))
{
tempVect.push_back(currVal);
}
std::cout << currVal << std::endl;
}
//the code never reaches this loop, since its stuck in the while loop
std::cout << "knak\n";
for (int i = 0; i < tempVect.size(); i++)
{
std::cout << tempVect[i];
}
}
I've tried doing multiple things like using std::cin.eof()/.fail(), std::cin >> currVal in the while loop, a do-while loop, but I can't seem to figure out how to get this to work. Does anyone have any tips on what to look into or how to approach this?

If your intention is to get all the input from a given line, and add individual numbers to the vector, then you want to read a line of input into a string, then use an istringstream to process that line of input.
A simplified example:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
int main() {
std::vector<int> tempVect;
std::string line;
std::getline( std::cin, line );
std::istringstream iss( line );
int curVal;
while ( iss >> curVal ) {
tempVect.push_back( curVal );
}
}

You should add a condition in your while loop so that it breaks when you want like:
if (temptVect.size() >= 5)
break;

I want the loop to stop looking for input after the last element is inputted or until a non-numberis inputted,
This loop should do exactly that.
while (std::cin >> currVal)
{
std::cout << "-\n";
if (!NumInVect(currVal, tempVect))
{
tempVect.push_back(currVal);
}
std::cout << currVal << std::endl;
}
This test while (std::cin >> currVal) will fail if read does not work and exit the loop. Since currVal is an integer, it will fail as soon as it tries to read something that is not an integer (note space and new lines are completely ignored).
What may be the issue is that std::cin is buffered. You can type a long time before filling up the buffer and causing the operator>> to do anything. To force this operator to read you have to force a flush this is usually done by hitting <enter> on the keyboard.
After the buffer is flushed it will read all the numbers on the input and exit the loop if a read fails. So you need to explicitly type not a number before hitting the <enter>.
eg:
1 2 3 4 5 X<enter>
or:
1<enter>
2<enter>
3<enter>
4<enter>
5<enter>
X<enter>
You need that non number to force the read to fail and the loop to exit.
Alternatively, you could say I want to read all the numbers on one line then exit the loop.
In that case: You should read a line and the processes the line. This is only a slight change from your above code.
std::string line; // Hold a line of text
std::getline(std::cin, line); // Read a line of text.
std::stringstream lineStream(line); // Convert string into stream
while (lineStream >> currValue) // Read each integer from the stream.
{ // Exits when the read fails to read
// a number becuase it is empty.
// The other stuff like before.
}

Related

Unknown number of strings (in letters) in the input

I want to write a program in which the names of n different chemical elements are read in the same line in the input (where 1 ≤ n ≤ 17 and n is also read in the input) (the names are made apart by a space). The names of the chemical elements should be stored in different strings for further uses.
As n is unknown, I don't know how to make something like an "array of strings". Of course I should not make 17 different strings st1,st2,st3,... :D.
Can you please help me? Any help will be high appreciated and they will help me a lot.
Thank you in advance.
It sounds like you want to read in a line and split it with spaces. Try something such as this:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
int main()
{
std::string input;
std::getline(std::cin, input); // takes one line, stops when enter is pressed
std::stringstream ss(input); // makes a stream using the string
std::vector<std::string> strings;
while (ss >> input) { // while there's data left in the stream, store it in a new string and add it to the vector of strings
strings.push_back(input);
}
for (std::string s : strings) {
std::cout << "string: " << s << std::endl;
}
}
You give input such as H He Li, terminated by hitting enter, and the strings are stored in strings (printed in last loop for demonstration).
Edit:
I now see that you want to read the number n in the input, too. In this case, you don't need the stringstream solution. You can do this instead:
int main()
{
int amount;
std::cin >> amount; // read in the amount
std::vector<std::string> strings;
for (int i = 0; i < amount; i++) {
std::string s;
std::cin >> s; // read in the nth string
strings.push_back(s); // add it to the vector
}
for (std::string s : strings) {
std::cout << "string: " << s << std::endl;
}
}
And pass input such as 3 H He Li.

Unexpected behavior of while loop in c++

Made something like this:
int main()
{
while (true)
{
std::cout << "Enter a number between one and nine. \n";
int oneandnine;
std::cin >> oneandnine;
if (std::cin.fail())
{
std::cin.clear();
std::cin.ignore(100, '\n');
std::cout << "INVALID UNPUT!\n";
}else if (oneandnine <= 9 && oneandnine >= 1)
{
break;
}else
{
std::cout << "INVALID UNPUT!\n";
}
}
return 0;
}
and when input is provided something like this 456aihdb, getting something like this:
INVALID UNPUT!
Enter a number between one and nine.
INVALID UNPUT!
Enter a number between one and nine.
Why does it loop twice like this? is it because when the 456 is discarded and the rest aihdb isn't which causes it to loop again and skip a cin input?
It is exactly as you think it is.
The fail flag isn't set immediately, instead the formatted input operator reads the integer 456 into oneandnine, but doesn't set the fail flag since it's a valid integer value. That leads to the else case executing since std::cin.fail() is false and oneandnine is not between 1 and 9.
The next iteration you read the invalid input and the fail flag will be set leading to the second error output.
One common way to handle validation is to read the whole line into a string, put that string into an std::istringstream and use that to attempt to parse the input:
if (!std::getline(std::cin, line))
{
// Failure of some kind, could be EOF or something else
// Probably best not to continue in this case
}
std::istringstream iss(line);
if (!(iss >> oneandnine))
{
// Invalid input, report it as such
}
if (oneandnine < 1 || oneandnine > 9)
{
// Invalid number, report it as such
}
// Correct input, continue with program
Note that input such as 6abc will be considered valid by the above code. The 6 will be extracted into oneandnine and the abc part will silently be discarded. If that's not wanted there are other ways for the parsing (e.g. std::stoi or std::strtol if exceptions are not wanted). Do that instead of the >> extraction, but the rest of the code above should be fine.
std::istream's operator >> doesn't read in whole lines. It reads until it finds an invalid character or whitespace, if it has found a valid character before the invalid character the read operation succeeds and the invalid character is left in the stream.
In your example the first iteration successfully reads 456 and leaves aihdb in the stream. This fails your range check and the second iteration then tries to read the remaining characters which fails as the first character isn't a number.
If you want to read whole lines use std::getline then parse the whole line into a number. For example:
#include <iostream>
#include <string>
using std::cout;
int main()
{
while (true)
{
std::cout << "Enter a number between one and nine. \n";
std::string line;
std::getline(std::cin, line);
int oneandnine;
size_t pos;
try
{
oneandnine = std::stoi(line, &pos);
}
catch ( std::exception& )
{
oneandnine = -1;
}
if (pos != line.size() || oneandnine > 9 || oneandnine < 1)
{
std::cout << "INVALID INPUT!\n";
}
else
{
break;
}
}
return 0;
}

How to read data from stdin, for a given number of test cases in C++

This might sound silly, but it's my first time solving programming contests on line. The problem is usually described as:
Input:
First line indicates the number of test cases t.For the next 't' lines the data is entered.
I've written the following program (with the correct headers included):
vector<string> read_strings(int t_cases) {
vector<string> ip_vec;
string line, str;
int cnt = 0;
while (cnt != t_cases-1) {
std::getline(std::cin, line);
++cnt;
}
std::istringstream iss(line);
while (iss >> str) {
ip_vec.push_back(str);
}
return ip_vec;
}
But this program always gets stuck in an input loop. I've also tried to parse the line as soon as it's entered by putting iss in the first while loop. If anyone could provide me a pointer on how to solve this problem, I will be able to finally test the rest of the program.
Thanks.
You need to add the lines to the vector as they are read. The first while loop is reading through all the test cases, but is not saving them somewhere. Then next while loop tries to reads line, which is the last test case read. Try the following:
vector<string> ip_vec;
string line, str;
for (int cnt = 0; cnt < t_cases; cnt++) {
std::getline(std::cin, line);
ip_vec.push_back(line);
}
Since you're starting to learn how programming contests work, I wouldn't recommend you to store all the input given. In general, you can store the number of inputs t and for each test case, the program outputs the answer for that test before reading the next test case. For example:
for (int i = 1; i <= t; i++) {
cin >> input;
// Some computations specific to the problem
cout << output << endl; // Pay attention on how the problem wants the output to be printed.
}
If the problem doesn't give you the number of test cases, you can simply change the loop to:
while (cin >> input) {
// Computations...
cout << output << endl;
}
It's usually how I solve problems from programming contests.
EDIT: As noted by #AnkitKulshrestha, if there's more than one input that you have to read for each test case, you can simply read them like this (with three inputs, for example):
while (cin >> a >> b >> c) {
// Computations...
cout << output << endl;
}

C++ file reading

I have a file that has a number in which is the number of names that follow. For example:
4
bob
jim
bar
ted
im trying to write a program to read these names.
void process_file(ifstream& in, ofstream& out)
{
string i,o;
int tmp1,sp;
char tmp2;
prompt_user(i,o);
in.open (i.c_str());
if (in.fail())
{
cout << "Error opening " << i << endl;
exit(1);
}
out.open(o.c_str());
in >> tmp1;
sp=tmp1;
do
{
in.get(tmp2);
} while (tmp2 != '\n');
in.close();
out.close();
cout<< sp;
}
So far I am able to read the first line and assign int to sp
I need sp to be a counter for how many names. How do I get this to read the names.
The only problem I have left is how to get the names while ignoring the first number.
Until then i cannot implement my loop.
while (in >> tmp1)
sp=tmp1;
This successfuly reads the first int from the and then tries to continue. Since the second line is not an int, extraction fails, so it stops looping. So far so good.
However, the stream is now in fail state, and all subsequent extractions will fail unless you clear the error flags.
Say in.clear() right after the first while loop.
I don't really see why you wrote a loop to extract a single integer, though. You could just write
if (!(in >> sp)) { /* error, no int */ }
To read the names, read in strings. A loop is fine this time:
std::vector<std::string> names;
std::string temp;
while (in >> temp) names.push_back(temp);
You'd might want to add a counter somewhere to make sure that the number of names matches the number you've read from the file.
int lines;
string line;
inputfile.open("names.txt");
lines << inputfile;
for(i=0; i< lines; ++i){
if (std::getline(inputfile, line) != 0){
cout << line << std::endl;
}
}
First of all, assuming that the first loop:
while (in >> tmp1)
sp=tmp1;
Is meant to read the number in the beginning, this code should do:
in >> tmp1;
According to manual operator>>:
The istream object (*this).
The extracted value or sequence is not returned, but directly stored
in the variable passed as argument.
So don't use it in condition, rather use:
in >> tmp1;
if( tmp1 < 1){
exit(5);
}
Second, NEVER rely on assumption that the file is correctly formatted:
do {
in.get(tmp2);
cout << tmp2 << endl;
} while ( (tmp2 != '\n') && !in.eof());
Although whole algorithm seems a bit clumsy to me, this should prevent infinite loop.
Here's a simple example of how to read a specified number of words from a text file in the way you want.
#include <string>
#include <iostream>
#include <fstream>
void process_file() {
// Get file name.
std::string fileName;
std::cin >> fileName;
// Open file for read access.
std::ifstream input(fileName);
// Check if file exists.
if (!input) {
return EXIT_FAILURE;
}
// Get number of names.
int count = 0;
input >> count;
// Get names and print to cout.
std::string token;
for (int i = 0; i < count; ++i) {
input >> token;
std::cout << token;
}
}

Advance File Pointer to skip over number in a file

I was wondering If I could jump positions in a text file.
Suppose I have this file.
12
8764
2147483648
2
-1
Whenever I try to read the third number it won't read because its larger than the max number for a 32 bit int.So whenever i reach the third number, it keeps reading the second over and over again. How can I jump to the 4th number?
Use std::getline instead of operator>>(std::istream, int)
std::istream infile(stuff);
std::string line;
while(std::getline(infile, line)) {
int result;
result = atoi(line.c_str());
if (result)
std::cout << result;
}
The reason you are experiencing the behavior that you are, is that when the std::istream tries (and fails) to read in an integer, it sets a "badbit" flag which means that something went wrong. As long as that badbit flag remains set, it won't do anything at all. So it's not actually re-reading in that line, it's doing NOTHING, and leaving the value that had been there alone. If you want to keep more in line with what you already had, it's probably like below. The above code is simpler and less error prone though.
std::istream infile(stuff);
int result;
infile >> result; //read first line
while (infile.eof() == false) { //until end of file
if (infile.good()) { //make sure we actually read something
std::cout << result;
} else
infile.clear(); //if not, reset the flag, which should hopefully
// skip the problem. NOTE: if the number is REALLY
// big, you may read in the second half of the
// number as the next line!
infile >> result; //read next line
}
You can first read the line, then convert the line to integer if you can. Here is an example for your file :
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
int main()
{
std::ifstream in("file");
std::string line;
while (std::getline(in, line)) {
int value;
std::istringstream iss(line);
if (!(iss >> value)) {
std::istringstream iss(line);
unsigned int uvalue;
if (iss >> uvalue)
std::cout << uvalue << std::endl;
else
std::cout << "Unable to get an integer from this" << std::endl;
}
else
std::cout << value << std::endl;
}
}
As an alternative to using std::getline(), you could call std::ios::clear(). Consider this excerpt from your previous question
fin >> InputNum;
You could replace that code with this:
fin >> InputNum;
if(!fin)
fin.clear();