I'm learning C++ and have a few questions.
This program should take inputs for the name and price of items and output them to a text file. When the sentinel value 999 is entered for the item name, the while loop should cease and output all sets of inputs (item name and price) to the text file.
I have two problems with this program:
Only the most recent set of inputs (name, price) is displayed. How do I keep all inputs in memory?
Entering 999 for the item name does not cause the program to exit. Instead, the program ceases to display prompts. How do I stop the program correctly?
I should probably use a for loop, but I'm not sure how that would be implemented.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
string item_name;
double price;
int item_number;
const string SENTINEL = "999";
ofstream myfile ("invoice1.txt");
while(item_name != SENTINEL)
{
cout<<"Enter the name of the item."<<'\n';
cin>>item_name;
if (item_name == SENTINEL)
{
cin>>item_name;
myfile<<"Thank you for your entries"<<'\n';
myfile<<item_name<<"#"<<price<<endl;
myfile.close();
break;
}
else
{
cout<<"Enter the price of the item."<<'\n';
cin>>price;
}
}
myfile<<"Thank you for your entries"<<'\n';
myfile<<item_name<<"#"<<price<<endl;
myfile.close();
return 0;
}
How to do this with a std::vector
First, a few includes:
#include <vector> // obviously. Can't do vectors without the vector header.
#include <limits> // for a trick I'll use later
Create a structure to link the item name with a price
struct item
{
string name;
double price;
};
and make a vector of that structure
vector<item> items;
Then after you read in a name and price, stuff it in a structure and stuff the structure into the vector.
item temp;
temp.name = item_name;
temp.price = price;
items.push_back(temp);
On why the while
loop doesn't work... That'll take a walk through.
while(item_name != SENTINEL)
This is a good start. If item_name
isn't SENTINEL
, keep going. Exactly right. Thing is, item-name hasn't been set the first time you get here, forcing some squirrelly logic inside the loop. General rule of thumb is read, then test. Test before read is not so useful. For one thing, there's nothing to test, but the real problem is now you're using untested data or have to include another test to catch it.
Read, then test.
{
cout<<"Enter the name of the item."<<'\n';
cin>>item_name;
Get an item name. Groovy-ish.
if (item_name == SENTINEL)
{
cin>>item_name;
OK. Not bad, but why get another item_name here?
myfile<<"Thank you for your entries"<<'\n';
myfile<<item_name<<"#"<<price<<endl;
myfile.close();
break;
break
exits a loop or a switch
. So out we go.
}
else
{
cout<<"Enter the price of the item."<<'\n';
cin>>price;
Reading in numerical values has a few dangers you have to watch out for. The big one is if whatever the user typed can't be turned into price
, cin
goes into error mode and won't get back out until the error is cleared. And before you try to get price again, the garbage data needs to be removed.
A neat thing about cin >> x
is it returns cin
. This lets you stack commands. cin>>a>>b>>c>>d
. cin
, and all its streaming brethren, have a built in boolean operator you can use in tests. If cin
is still in a good state, all of the reads completed successfully, it can be tested and will return true
.
This lets you do stuff like if (cin>>a>>b>>c>>d)
and test that all of the reads were good in one shot.
}
}
Applying read, then test, we get
while(cin>>item_name && // read in an item name
item_name != SENTINEL) // and then test that it isn't the sentinel
This goofy-looking bit of code is about the safest way to do this. It will even catch and exit cin
comes to a sudden end. Doesn't happen all that often with cin
, but this is a great way to test for the end of a file.
{
while (!(cin >> price)) // keep looping until the user gives a number
{ // if we're here, the user didn't give a good number and we have to clean up
// after them. Bad user. Bad. Bad.
As an aside, don't do this trick with a file. The file could have ended. You would have to test the file for end of file and exit here before continuing with the clean up.
// clear the error
cin.clear();
// throw out everything the user's typed in up to the next line
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
// user got out of the while of doom. They must have entered a number.
Welllll actually, it just had to start with a number. cin >>
is pretty dumb and will let 1234abcd through, taking the 1234 and leaving the abcd for the next read. This can leave you with some nasty surprises. In this case the abcd will wind up as the next item_name
. what should have been the next item_name
will become the next price
and the bad ju-ju rolls on from there.
And now back to the code.
item temp; // make a temporary item
temp.name = item_name; // set the values correctly
temp.price = price;
items.push_back(temp); // put them in the list.
You can save a bit of work here by adding a constructor to item
and using vector
's emplace_back
method. Look it up. Very handy.
}
And again without the running commentary:
while(cin>>item_name && // read in an item name
item_name != SENTINEL) // and then test that it isn't the sentinel
{
while (!(cin >> price)) // keep looping until the user gives a number
{ // if we're here, the user didn't give a good number and we have to clean up
// after them. Bad user. Bad. Bad.
// clear the error
cin.clear();
// throw out everything the user's typed in up to the next line
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
// user got out of the while of doom. They must have entered a number.
item temp; // make a temporary item
temp.name = item_name; // set the values correctly
temp.price = price;
items.push_back(temp); // put them in the list.
}
Now you have a vector
full of item
s to print. Stack Overflow is full of examples on how to do this, but it's best if you take a shot at it first.
Why do you have an extra cin call inside the if statement checking the entry? I think that is not necessary.
For the issue with your inputs you are only storing the most recent entered values because each time the loop runs again it will over write the variables.
To solve this you will need to use an array to store items. If you want to make it so that you can run the loop an enter as many inputs as needed you will need to implement a dynamic array.
Here is how to implement a dynamic array http://www.learncpp.com/cpp-tutorial/6-9a-dynamically-allocating-arrays/
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.