How to properly terminate input - c++

Currently working on an exercise from Stroustrup's intro to C++ book and it asks: Write a function that given two vectors price and weight computes a value (an “index”) that is the sum of all price[i]*weight[i]. Make sure to have weight.size()==price.size()
vector<double> weight;
vector<double> price;
void read_weight() {
cout << "Input weight" << '\n';
for (double weights; cin >> weights; ) {
weight.push_back(weights);
if (!(cin >> weights)) {
cin.clear();
cin.ignore(numeric_limits<double>::max(), '\n' );
break;
}
}
}
void read_price() {
cout << "Input price" << '\n';
for (double prices; cin >> prices; ) {
price.push_back(prices);
if (!( cin >> prices)) {
cin.clear();
cin.ignore( numeric_limits<double>::max(), '\n' );
break;
}
}
}
void calculate() {
double sum = 0;
for (int i = 0; i < price.size(); ++i)
sum += price[i] * weight[i];
cout << "Sum is " << sum << '\n';
}
int main() {
read_weight();
read_price();
calculate();
}
This is the code I currently have, but I cannot figure out how to terminate input properly. In previous chapters, his book mentions you could use an input besides a double to terminate (like |). However, I learned that this only puts cin into the failed state and does not allow me to move onto the price function. Unfortunately, the book does not cover how to handle this as of yet so I just copied code about cin.clear and cin.ignore in hopes that it would work. But this did nothing. I noticed that I was actually allowed a single input if I were to change the vector to int instead of double and am perplexed for the difference in behavior. I was wondering if anyone could give me tips on how to fix this?

You were halfway there already. I've written a commented replacement for read_weights(); you should be able to take it from there.
#include <vector>
#include <limits>
#include <iostream>
// Use return values instead of working on global variables.
// Avoid using global variables whenever possible, they are a
// maintenance pain.
std::vector< double > read_weights()
{
std::vector< double > weights;
double weight;
// You can embed the \n right in the string, no need to put it as a
// separate character.
std::cout << "Input weights; enter a non-number to terminate input.\n";
// If anything not double is entered, cin goes into fail state --
// that is our terminating condition right there, so use it!
while ( std::cin >> weight )
{
weights.push_back( weight );
}
// Clean up cin
std::cin.clear();
// Use the correct type for max(); you had 'double' here...
cin.ignore( numeric_limits< std::streamsize >::max(), '\n' );
// Don't worry about the apparent copying of the vector upon return.
// Any modern compiler should be able to optimize this away.
return weigths;
}
A simple main() for testing:
int main()
{
std::vector< double > weights = read_weights();
std::cout << "Vector contents:\n";
for ( auto & v : weights )
{
std::cout << v << "\n";
}
}
Now all you have to add is a read_price()... now wait, you don't, do you? Because all you're actually doing is the very same thing as in read_weights(), entering doubles! So move the input prompt out of read_weights() and make it one function, read_values(), which you call twice, once to get weights and once to get prices...
int main()
{
std::cout << "Enter weights; enter a non-number to terminate input.\n";
std::vector< double > weights = read_values();
std::cout << "Enter prices; enter a non-number to terminate input.\n";
std::vector< double > prices = read_values();
// ...
}
For the calculate function, use references for the parameters so the vectors don't have to be copied:
void calculate( std::vector<double> & weights, std::vector<double> & prices )
And once you got this all running, keep in your mind that, later on, you will (or at least should) be learning about <algorithm>, functors, and lambdas... which should remove the need for calculate and replace it with an elegant one-liner... but that's yet to come, and I don't want to confuse you with that at this point.

You can use std::getline to read a whole line from cin into a string. Thereafter, you need to determine whether the line that has been entered is a double or not. I normally wouldn't recommend going too wild with regular expressions (I don't use them a lot in C++), but in this case I reckon it is a pretty simple and sound solution. If the string is in the format "32.23" (digits dot digits), you convert it to a double using std::stod, push that to the vector and continue to read from cin. If it is not, you break the loop and go on with the program flow.
Stay away from using global variables, use local ones and pass them arround.
Also, notice that your functions read_price and read_weight are almost identical. In such a case, you definitely only want to write a single (parameterized) function. In this case, you don't even need parameters, you can just use the same function for both.
(You could also directly read your values into double variables from a stream (std::cin), which might be considered more elegant as you need less conversion, however the method below is easy and you don't need to worry what is being entered into std::cin)
#include <vector>
#include <string>
#include <iostream>
#include <regex>
std::vector<double> get_doubles_from_cin(){
std::vector<double> doubles;
std::regex double_regex ("\\d+\\.\\d+");
std::string input;
while(std::getline(std::cin, input)){
if (std::regex_match(input, double_regex)){
doubles.push_back(std::stod(input));
}
else{
break;
}
}
return doubles;
}
void calculate(std::vector<double>& weights, std::vector<double>& prices) {
double sum = 0;
for (int i = 0; i < prices.size(); ++i) {
sum += weights[i] * prices[i];
}
std::cout << "Sum is " << sum << '\n';
}
int main() {
std::cout << "Enter weights" << std::endl;
auto weights = get_doubles_from_cin();
std::cout << "Enter prices" << std::endl;
auto prices = get_doubles_from_cin();
calculate(weights, prices);
}

Related

How to determine number of values added to vector in C++?

I'm trying to build a little task using C++ in which I need to allow the user to determine up front how many gross_paychecks they would like to place in a vector called 'gross_paychecks_vector'.
So far this is what I have:
vector<double> gross_paychecks_vector (5);
double gross_paychecks;
// Add 5 doubles to vector
cout << "Please enter an integer" << endl;
cin >> gross_paychecks;
for(gross_paychecks = 0; gross_paychecks <= gross_paychecks_vector; ++gross_paychecks ){
cin >> gross_paychecks;
}
Right now I'm somewhat lost because I'm not sure whether to switch the vector to something like vector<double> gross_paychecks {} because it throws an error in the for loop.
Also I'm not sure how to go with the for loop (should I actually use a for-loop or something else?). I need to accept input from the user as long as it has not met the numbers of gross_paychecks that he/she has specified.
You probably want this:
vector<double> gross_paychecks_vector; // initially the vector is empty
...
cout << "How many paychecks:" << endl;
cin >> gross_paychecks;
for (int i = 0; i < gross_paychecks; i++)
{
double value;
cin >> value;
gross_paychecks_vector.push_back(value); // add new value to vector
}
// display values in vector
for (auto & value : gross_paychecks_vector)
{
cout << value << "\n";
}
Additionally. If you want to use modern C++ features, you would use:
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
int main()
{
std::vector<double> grossPaychecks{};
std::cout << "How many paychecks:\n";
size_t numberOfPaychecks{0};
std::cin >> numberOfPaychecks;
// Read all data
std::copy_n(std::istream_iterator<double>(std::cin),numberOfPaychecks, std::back_inserter(grossPaychecks));
// Print all data
std::copy(grossPaychecks.begin(), grossPaychecks.end(), std::ostream_iterator<double>(std::cout,"\n"));
return 0;
}

Storing dynamic array in vector

I have to display a histogram of a student's grades. I've stored the grades in a dyn. array, but my goal is to store them in a vector. What's the right way of going about this? Hope this makes sense.
Edit:
My attempt at using a vector
void displayHistogram(int minGrade, vector<int> ptrV) {
cout << endl;
for (int i = 0; i <= minGrade; i++) {
if (ptrV[i] != 0) {
cout << "Number of " << i << "'s: " << ptrV[i] << endl;
}
}
}
void histogram() {
int minGrade = 0, grade;
const int grade_max = 100;
vector<int> ptrV(grade_max, 0);
cout << "Enter the student's grades (-1 to stop entering): \n";
do {
cin >> grade;
if (grade > minGrade) {
minGrade = grade;
}
if (grade >= 0) {
ptrV.push_back(grade);
}
} while (grade != -1);
displayHistogram(minGrade, ptrV);
}
Your basic mistake is that you try to force the vector as if it was a raw array. It does stuff for you, let it. It knows it's size, for instance. You don't need
void displayHistogram(int minGrade, vector<int> ptrV) {
cout << endl;
for (int i = 0; i <= minGrade; i++) {
Instead, you can use vector::size:
void displayHistogram(vector<int> ptrV) {
cout << endl;
for (size_t i=0; i<ptrV.size(); i++) {
(Even better: void displayHistogram(const vector<int>& ptrV) to signify that ptrV is not changed here and to avoid copying it every time you call the function by using a reference.)
(If you wouldn't use the i as it is the grade and if you have a newer compiler, I'd recommend a for each loop instead. Those are usually the way to go, just happens that you have one of the rarer cases in which it isn't.)
Likewise, you first set the size of your vector and then increase it, which to me means that you do not trust it:
vector<int> ptrV(grade_max, 0);
At this point, you have a vector with a hundred entries that are all zero. You don't need to resize it later if a hundred entries is all you need. vector::push_back resizes it. But note that setting it to a size of 100 means that [100] is not a valid position, the last one is [99], as we start to count at zero. You'd need to set it to a size of 101 to have both zero and hundred as valid addresses.
I'd change your code to:
const int grade_max = 100;
vector<int> ptrV(grade_max+1, 0); //changed it to +1 here as prtV[100] should be legal
cout << "Enter the student's grades (-1 to stop entering): \n";
while (true)
{
int grade; // put stuff in the smallest scope possible
cin >> grade;
if(grade == -1) break; // doing that here means we don't have to think about it anymore - the do while does it at last, I do it at first, handling all the special cases at the start and then assume I have the regular case.
if(grade < 0 or grade > grade_max) continue; // continue jumps to the top of the most inner loop. Note that I make sure to catch illegal but possible input.
ptrV[grade] += 1; // personal preference, I use ++ only to iterate
}
displayHistogram(ptrV);
I rewrote the structure, using a while(true), I think the way I did it is more intuitive, but there will be people who disagree with that and who would also write things like
if(grade == -1)
{
break;
}
and there are some good arguments for that, mostly a good practice routine, always doing the braces to avoid errors. However, I prefer a one liner to reduce verbosity.
One improvement would also be to tell the user about bad input:
if(grade < 0 or grade > grade_max)
{
cout << "Input not in valid range. Please choose number within 0 to " << grade_max << endl;
continue;
}
Now, another big thing to do here would be to leave the procedural part, by the way.
Go for a class GradeHistogram which has all those functions as a part of it, being called like
GradeHistogram histogram;
histogram.take_input();
histogram.display();
but that is for when you get your code working.
(My answer became more of a review as found on CodeReview, but I think that this is what you need rather than small fixes. This is something I can only recommend you, by the way, putting your code on CodeReview once it works.)
but my goal is to store them in a vector.
The issue seems to be that you've already sized the vector to hold grade_max entries. However when filling the vector, you are using push_back. By using push_back, you are adding more entries to the end of the vector, which is not what you want to do.
The solution is either
Change this vector<int> ptrV(grade_max, 0); to this vector<int> ptrV; and leave the calls to push_back alone, or
Keep vector<int> ptrV(grade_max, 0); but instead merely use ptrV[i] = grade;
If what you want to show is a histogram, then the easiest thing to do would be to use a std::map from grade to count of grade.
Something like this:
#include <iostream>
#include <map>
int main() {
std::cout << "Enter the student's grades (-1 to stop entering): \n";
std::map<int, int> grades_map;
int input_grade = -1;
do {
cin >> input_grade;
if (input_grade > -1) {
grades_map[input_grade]++;
}
} while (input_grade != -1);
// Print histogram
for (const auto& [grade, count] : grades_map) {
std::cout << "Students with grade << grade << ": ";
for (int i = 0; i < count; ++i) {
std::cout << '*';
}
std::cout << '\n';
}
}

modify number of variables in istringstream

I need to get input in a line based on a modifiable number of variables that are determined from an earlier input.
If the input you get in an earlier function is 1, the the code would be like
std::string istring;
std::getline(std::cin, istring);
std::istringstream stream(istring);
while (stream >> a)
{
statement;
}
is it possible to make a condition for while loop that changes depending on what your input is ? so that if the input was for example 5, it would behave like
while (stream >> a >> a >> a >> a >> a)
{
statement;
}
I tried doing while ((stream>>a)*number) but that doesn't work
Specific Solution
First to answer your specific question:
I need to get input in a line based on a modifiable number of variables that are determined from an earlier input.
The solution in C++ to that is a combination of a istringstream, a while loop and a vector:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
int main() {
unsigned int earlierInput = 5; // this is the earlier input
std::vector<int> numbers; // The type of vector also controls what
// data type you want the user to give
// you. Because the stream will try to
// the user input into that type.
while (numbers.size() != earlierInput) {
// Here we ask the user for input and convert the input
// to a stream, like that we can try to convert the user
// input into the data type we want.
std::string istring;
std::cout << "Please type in " << earlierInput << " numbers: ";
std::getline(std::cin, istring);
std::istringstream reader(istring);
// read as many numbers as possible.
for (int number; reader >> number;) {
numbers.push_back(number);
}
if (numbers.size() != earlierInput) {
// if not enough numbers empty vector and print
// error message
std::cout << "Not the right amount of numbers!";
std::cout << "Try again!" << std::endl;
numbers.clear();
}
}
// If we get here the user typed in the right amount of numbers
std::cout << "You typed the following numbers:" << std::endl;
for (const int& i : numbers) {
std::cout << i << std::endl;
}
}
This is for exactly one user input. If you want to ask the user an arbitrary amount of times to do this (e.g you want to ask the user 10 times for 5 numbers), then you need to apply the rule above again and as such stack up:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
int main() {
unsigned int amountOfInputs = 2; // this is the amount of times
// you want the user to type
// something in
unsigned int earlierInput = 5; // this is the earlier input
std::vector<std::vector<int>> allNumbers; // Now we need a vector in
// a vector to store the results.
// The type of the inner vector also
// controls what data type you want the
// user to give you.
while (allNumbers.size() != amountOfInputs) {
std::vector<int> numbers; // This is the vector as in the example
// above.
while (numbers.size() != earlierInput) {
// Here we ask the user for input and convert the input
// to a stream, like that we can try to convert the user
// input into the data type we want.
std::string istring;
std::cout << "Please type in " << earlierInput << " numbers: ";
std::getline(std::cin, istring);
std::istringstream reader(istring);
// read as many numbers as possible.
for (int number; reader >> number;) {
numbers.push_back(number);
}
if (numbers.size() != earlierInput) {
// if not enough numbers empty vector and print
// error message
std::cout << "Not the right amount of numbers!";
std::cout << "Try again!" << std::endl;
numbers.clear();
}
}
// If we get here the user typed in the right amount of numbers
// and we can save them and clear the array for using it again.
allNumbers.push_back(numbers);
numbers.clear();
}
std::cout << "You typed the following numbers:" << std::endl;
unsigned int round = 1;
for (auto numbersOfRound : allNumbers) {
std::cout << "For round " << round++ << ": ";
for (auto i: numbersOfRound) {
std::cout << i;
}
std::cout << std::endl;
}
}
Theory
Why Vectors
To save an arbitrary amount of data and be able to access it, you must use a dynamic allocated array or anything alike. That is because at compile time you don't know how many variables you will need at runtime and as such you cannot give all of them a name.
Why streams
In theory a stream is a possibly infinit long string or data (see also here). A user input is therefor a stream as it may theoretically be infinit long. Though this does not apply in praxis.
To extract informations from a stream one has to use the >> operator, also known as the extraction operator. This operator does not support vectors as an (rhs) operand as stated in the documentation here. It supports only basic datatypes (that is why we need a temporary variable int number in the examples above).

How do I check the type of data read on cin?

More specifically, I want to make sure this type is a double (or int/float). I have searched near and far and found pretty much zero non-rigorous solutions. I want to only use code that I can fully understand, being a complete beginner at C++.
In Java, I can simply do something like this:
ArrayList<Double> data = new ArrayList<Double>();
Scanner in = new Scanner(System.in);
while (in.hasNextDouble()) {
double x = in.nextDouble();
data.add(x);
n += 1;
sum += x;
}
double average = sum / n;
C++ is clearly a different story. Here's what I tried:
vector<double> data;
while (looping)
{
cin >> x;
if (typeid(x) != typeid(float) && typeid(x) != typeid(double) &&
typeid(x) != typeid(int))
{looping = false; break;}
data.push_back(x);
n += 1;
sum += x;
}
double average = sum / n;
This doesn't work. The loop keeps going as doubles are entered, but once I enter something that's not a double the code simply stops and doesn't advance onward. I'm pretty crestfallen. Any suggestions?
User doesn't "enter a variable". She enters a string. And operator >> when supplied with a float variable tries to interpret this string as a textual representation of a floating-point number.
It may or may not succeed: in Java, failure would cause nextDouble() to throw an exception. In C++, stream operations don't throw; instead failure means that cin.good() will start to return false.
float x;
cin >> x;
while (cin.good())
{
data.push_back(x);
n += 1;
sum += x;
cin >> x;
}
if x is declared as double, your if condition would fail and hence the while loop is broken
You have several solutions here. I will post 2 of them. The first solution needs c++11 standard due to stod function. You can do this passing -std=c++11 flag to gcc or clang++. Microsoft compiler enables c++11 by default.
Solution 1. It consists of reading in a string all the time through cin >> input_string and using standard c++11 function stod. stod stands for string to double. If stod fails to parse a double, it will throw an std::invalid_argument exception.
This solution would be like this:
#include <iostream>
#include <vector>
#include <numeric>
#include <string>
#include <stdexcept>
using namespace std;
int main() {
vector<double> vec;
string input;
try {
while (getline(cin, input)) {
vec.push_back(stod(input));
}
}
catch (std::invalid_argument &) {
cout << "Invalid argument caught\n";
//Failed to parse a double
}
//If pressed Ctrl+D (in linux, which sends EOF), failbit and eofbit are set
//If it is through invalid_argument the way the loop was exit, then,
//eof and failbit are not set and cin can be used without clearing.
double average = accumulate(vec.begin(), vec.end(), 0.0)/vec.size();
cout << "EOF: " << cin.eof() << endl;
cout << "Fail: " << cin.fail() << endl;
//Clean eof and failbit to be able to use cin again
cin.clear();
cout << "EOF after cleaning: " << cin.eof() << endl;
cout << "Fail after cleaning: " << cin.fail() << endl;
cout << average << endl;
}
EDIT: I tested and when you put more than one number per line it will just get the first one without throwing std::invalid_argument. It will throw an std::invalid_argument only when you start a line when the line starts with a non-double. This is because the stod function behaves like this: stod reference.
Note that this solution only allows to read one double per line.
Solution 2. Read using cin >> input_double directly. This can fail. Please note that iostreams don't use exceptios by default in c++. You can activate them with the api, but I don't recommend you to do it because you can manage all error handling locally.
You can read any number of doubles separated by any space characters:
#include <iostream>
#include <vector>
#include <numeric>
#include <limits>
using namespace std;
int main() {
double x = 0.0;
vector<double> data;
//cin will fail to read the first non-double in the input.
//You can input as many doubles as you wish. Spaces will
//be ignored. At the first non-double, cin will fail to read
//and will exit the loop, setting istream::failbit.
//Alternatively, you can send EOF (Linux is Ctrl+D) and the loop also will finish.
while (cin >> x) {
data.push_back(x);
}
double average = accumulate(data.begin(), data.end(), 0.0)/data.size();
//If you wanted to use again cin, you should:
//1. Clear failbit. You can do like this:
cin.clear();
//2. Cleaning the remaining input. Will stop when finding end of line.
string rubbish;
geline(cin, rubbish);
//cin is usable here again if you need it and with the input clean already.
cout << average << '\n';
}
You can give something like this in the input, in one line:
1 2.4 -38.7 5.8 28.9 hello.
What will happen? The loop will consume until 28.9, stopping at hello. After that, failbit is set. We clean failbit to be able to continue reading until the end of the line. Since hello is considered "rubbish" because we wanted to read doubles, we clean it with a getline and we can use cin again without trouble.

How to make cin >> not convert float to integer?

I have the following simple code:
#include <iostream>
int main()
{
int a;
std::cout << "enter integer a" << std::endl;
std::cin >> a ;
if (std::cin.fail())
{
std::cin.clear();
std::cout << "input is not integer, re-enter please" <<std::endl;
std::cin >>a;
std::cout << "a inside if is: " << a <<std::endl;
}
std::cout << "a is " << a <<std::endl;
std::cin.get();
return 0;
}
When I run the above code and input: 1.5, it outputs: a is 1. FYI: I compile and run the code with gcc 4.5.3.
This means that if cin expects an integer but sees a float, it will do the conversion implicitly. So does this mean that when cin sees a float number, it is not in fail() state? Why this is the case? Is it because C++ does implicit conversion on >> operator?
I also tried the following code to decide whether a given input number is integer following idea from this post: testing if given number is integer:
#include <iostream>
bool integer(float k)
{
if( k == (int) k) return true;
return false;
}
int main()
{
int a;
std::cout << "enter integer a"<< std::endl;
std::cin >> a ;
if (!integer(a))
{
std::cout << "input is not integer, re-enter please" ;
std::cin.clear();
std::cin >> a;
std::cout << "a inside if is: " << a <<std::endl;
}
std::cout << "a is " << a <<std::endl;
std::cin.get();
return 0;
}
This block of code was also not able to test whether a is integer since it simply skip the if block when I run it with float input.
So why this is the case when getting user input with cin? What if sometimes I want the input to be 189, but typed 18.9 by accident, it will result in 18 in this case, which is bad. So does this mean using cin to get user input integers is not a good idea?
thank you.
When you read an integer and you give it an input of 1.5, what it sees is the integer 1, and it stops at the period since that isn't part of the integer. The ".5" is still in the input. This is the reason that you only get the integer part and it is also the reason why it doesn't seem to wait for input the second time.
To get around this, you could read a float instead of an integer so it reads the whole value, or you could check to see if there is anything else remaining on the line after reading the integer.
When reading user input I prefer not to use operator>> as user input is usally line based and prone to errors. I find it best to read a line at a time and validate:
std::string line;
std::getline(std::cin, line);
This also makes it easy to check for different types of numbers.
std::stirngstream linestream(line);
int val;
char c;
if ((linestream >> val) && !(linestream >> c))
{
// Get in here if an integer was read.
// And there is no following (non white space) characters.
// i.e. If the user only types in an integer.
//
// If the user typed any other character after the integer (like .5)
// then this will fail.
}
Of course boost already supports this:
val = boost::lexical_cast<int>(linestream); // Will throw if linestream does
// not contain an integer or
// contains anything in addition
// to the integer.
Boost of course will convert floats as well.
I have some snippet which is kind a poor coding, but it works.
This method is pretty simple, but doesn't handle case when input value is invalid.
See more: https://en.cppreference.com/w/cpp/string/byte/atof
static float InputFloat(std::string label)
{
std::string input;
std::cout << label;
std::cin >> input;
return atof(input.c_str());
}
int main()
{
float value = InputFloat("Enter some float value: ");
std::cout << "value = " << value;
return 0;
}