SO when my program starts, it attempts to read a list of products from a file. but if the file does not exist it displays an error and continue on. the problem im having is when it displays the error, it doesnt continue on to the do while loop
ifstream input;
input.open("data.txt");
if (input.fail())
{
cout << "\n Data file not found \n";
}
ListItemType data;
input >> data.productname;
while(( !input.eof()))
{
input >> data.category;
input >> data.productprice;
addproduct(head, data);
input >> data.productname;
}
input.close();
It's not identical functionality, but it's generally better to move towards something like:
if (std::ifstream input("data.txt"))
{
ListItemType data;
while (input >> data.productname >> data.category >> data.productprice >> data.productname)
addproduct(head, data);
if (!input.eof())
std::cerr << "Error parsing input file.\n";
}
else
cout << "\n Data file not found \n";
If you structure your if/else clauses as above, whatever happens it will continue to the following code as you'd like.
Note that the code above checks for a problem after each input operation. Your code tries to read data.productprice even if reading data.category failed. It's kind of weird you're reading productname twice, and I'm assuming you can call addproduct after the I/O - if not you'll need a while loop like:
while (input >> data.productname >> data.category >> data.productprice)
{
addproduct(head, data);
if (!(input >> data.productname))
break;
}
Related
In my case, I have to make sure the user input is either 1 or 2, or 3.
Here's my code:
#include <iostream>
using namespace std;
void invalid_choice_prompt() {
string msg = "\nInvalid Command! Please try again.";
cout << msg << endl;
}
int ask_user_rps_check_input(int user_choice) {
if (user_choice == 1 || user_choice == 2 || user_choice == 3) return 1;
return 0;
}
int ask_user_rps() {
// ask user's choice of Rock or Paper or Scissors
while (1) {
string msg =
"\nPlease enter your choice:\nRock - 1\nPaper - 2\nScissors - 3";
cout << msg << endl;
int user_choice;
cin >> user_choice;
if (ask_user_rps_check_input(user_choice)) {
return user_choice;
}
invalid_choice_prompt();
}
}
int main() {
ask_user_rps();
return 0;
}
The code is capable to handle the situation when the input is an integer, but when the input are characters or strings, the program will be trapped in the infinite loop.
Is there any elegant way to do this? I've found some methods about using cin.ignore to ignore the specified length of io buffer, but I don't think this method is flexible enough. I am looking for a more flexible solution.
I think an option would be to collect the user input to a string and then move it to stringstream using getline kind of like this:
std::string input;
std::getline(std::cin, input);
//Now check if the input is correct. if it is, then:
std::stringstream stream;
stream << input;
int num;
stream >> num;
I'm not sure if this is a good method but it works.
One of the simplest solution would be to check the cin stream failure something like below:
int ask_user_rps() {
// ask user's choice of Rock or Paper or Scissors
while (1) {
string msg =
"\nPlease enter your choice:\nRock - 1\nPaper - 2\nScissors - 3";
cout << msg << endl;
int user_choice;
cin >> user_choice;
if(cin.fail()) {
invalid_choice_prompt();
std::cin.clear();
std::cin.ignore(256,'\n');
continue;
}
if (ask_user_rps_check_input(user_choice)) {
return user_choice;
}
invalid_choice_prompt();
}
}
Reading from a stream using operator >> takes as many characters from the stream as the target type accepts; the rest will remain in the stream for subsequent reads. If the input has a format error (e.g. a leading alphabetical characters when an integer is expected), then an error-flag is set, too. This error-flag can be checked with cin.fail(). It remains set until it gets explicitly cleared. So if your code is...
int user_choice;
cin >> user_choice;
and if you then enter something that is not a number, e.g. asdf, then user_choice has an undefined value, an error-flag cin.fail() is (and reamins) set. So any subsequent read will fail, too.
To overcome this, you have to do three things:
First, check the error-flag. You can do this either through calling cin.fail() after a read attempt of through checking the return value of the expression (cin >> user_choice), which is the same as calling cin.fail().
Second, in case of an error, you need to clear the error-flag using cin.clear(). Otherwise, any attempt to read in anything afterwards will fail.
Third, if you want to continue with reading integral values, you need to take the invalid characters from the stream. Otherwise, you will read in asdf into a variable of type integer again and again, and it will fail again and again. You can use cin.ignore(numeric_limits<streamsize>::max(),'\n'); to take all characters until EOF or an end-of-line from the input buffer.
The complete code for reading an integral value with error-handling could look as follows:
int readNumber() {
int result;
while (!(cin >> result)) {
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(),'\n');
cout << "Input is not a number." << std::endl;
}
return result;
}
Take input as char
string user_choice;
cin >> user_choice;
check input is valid or not if(user_choice=='1')
I have just a couple issues here with my code. It works but I'm not advanced enough to do what I want to yet. Nor do I know how to word it for a google search. I have a Blackjack game that I'm doing and want to know how to edit certain lines of the file depending on user input. Simpler, I want a user to be able to open the game and start with their balance from the last time they were playing(a save and load feature). My issues are the balance and the username are on the same line in the text file (purposefully) and I want to assign the variables to those in the text file. I know I'm on the right track, I just dont know where to go from here. Thanks for the help in advance. If I broke a rule of posting, I'm sorry.
input username
if username is present in file
edit balance on leave
if username isnt present in file
create new user
Here is my code for the load function:
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
string word;
for (int x = 0; in >> word; x++);
{
user1.name = word;
user1.balance = word;
}
cout << user1.name << endl;
cout << user1.balance << endl;
in.close();
}
else
cout << "Cannot open a file";
}
void save(userAcc user1)
{
user1.balance = "1000";
cout << "Enter a username: ";
cin >> user1.name;
ofstream out;
out.open("Balances.txt", ios_base::app);
if (out.is_open())
{
out << user1.name << " " << user1.balance << endl;
out.close();
}
else
cout << "Cannot open a file";
}
In
for (int x = 0; in >> word; x++);
remove the trailing ;. It ends the statement before the body of the for loop, separating the two. The for spins around doing nothing but reading the file until it ends and incrementing the unused variable x and the following code block will be run exactly once, storing whatever is in word (and since the loop will exit when the read into word fails, what's in word will depend on the C++ Standard version the the compiler's been set to use) into user1.
Once the ; is removed, the for loop will read into word until no more words can be read from the file. Every word read is copied into the same userAcc writing over the previous word. When the file hits the end in >> word will fail and the loop will exit. The last word in the file will then be printed out, all other words having been overwritten.
Naïve fixing of this would look something like
void load(userAcc user1)
{
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
while (in >> user1.name // read name in from file
>> user1.balance) // read balance in from file
{ // loop will exit when it cannot read a name and a balance from the file
// for now we're just printing out what's read from the file.
cout << user1.name << endl << user1.balance << endl;
}
// in.close(); not needed. File will automatically close when in goes out of scope.
}
else
cout << "Cannot open a file";
}
But we probably want to do more than print out all of the users in the file, so let's put them into a convenient resizable container like std::vector.
vector<userAcc> load() // takes no parameters, returns list of accounts
{
vector<userAcc> accounts;
ifstream in;
in.open("Balances.txt");
if (in.is_open())
{
userAcc user1; // account we can read into
while (in >> user1.name >> user1.balance)
{
accounts.push_back(user1); // store account
}
}
else
cout << "Cannot open a file";
return accounts; // hand accounts read back to caller.
}
Use of the function would be something like
vector<userAcc> accounts = load();
The save function looks pretty much good-to-go as written.
I am trying to learn file i/o in c++. I have a bunch of variables like kill, death, assist, result, notes and I want to enter them into a text file. I have seen tutorials where they use cin inside a while loop like
while ( cin >> kill >> death >> assist ) {
file << kill << death << assist ; }
but I want the user to type in "done" when they are done entering their input.
I know in python you can do something like
while (input != "done" ):
//code//
can I do something similar?
while ( cin >> () != "done" ) {
cin >> kill >> death >> assist >> notes ;
file << kill << death << assist << notes ; }
You can have multiple conditions using the logical AND and OR operators.
You can also use std::getline to read a line, and then use an input string stream to parse the input.
For example something like:
std::string input;
while (std::getline(cin, input) && input != "done")
{
std::istringstream is(input);
is >> kill >> death >> assist;
...
}
The expression in the condition reads a while line, checks that there was no errors or end-of-file when reading, and then check that the input was not "done".
I have a program where I need to take in a command line argument that is the name of a text file. The program is run by ./programName file1. If for the purpose of my program I can assume file1 will always be formatted correctly, can I just do
ifstream myFile(argv[1]);
if(!myFile.good()){
cout << "Not a valid player file" << endl;
return 1;
}
to check if there is such a file, and then simply
myFile >> var1;
myFile >> var2;
myFile.close();
or should I stick with
if(myFile.is_open()){
myFile >> var1;
myFile >> var2;
myFile.close();
}
else{
cout << "Unable to open file";
}
Again I can assume the file will always be formatted correctly(i.e myFile >> var# will always work). So is there any situation in which the else statement might be called after my initial check of (!myFile.good())?
You should mainly check the input operations, to know if your file has the proper input format:
if(myFile >> var1 >> var2) {
// Everything's fine
}
else {
// WRONG FORMAT OR COULDN'T BE OPENED!
}
To know the actual error reason in the else part, you can inspect the iostate flags returned by the std::istream::rdstate() function.
If the open() failed the badbit will be set, if one of the input operations failed the failbit will be set.
I have this text sample
Ahmed 10
kmal 5
doola 6
And I am using this code to read it
if (myfile.is_open())
{
while ( myfile.good() )
{
myfile >> name;
myfile >> phone;
cout << name <<" "<<phone<<endl;
}
myfile.close();
}
I get this output
Ahmed 10
kmal 5
doola 6
doola 6
Why does this code read the last line twice ?
myfile.good() becomes false AFTER the read fails. Same with myfile.eof().
What you want is basically:
myfile >> name;
myfile >> phone;
if (!myfile.good()) break;
And this can be shortened to:
if (!(myfile >> name >> phone)) break;
or finally:
while (myfile >> name >> phone) { ... }
Try
while( myfile >> name >> phone ) {
// your code here
}
I believe the problem with the other approach is that eof isn't signaled until you actually try to read more than you should. That is, when you attempt to myfile >> name on the last round. That fails, as does myfile >> phone and only then do you break out of the loop.
Don't use while(myfile.good()). It will still say true, even when you have read the last line in your file. And then the next >> will fail, leaving the name and phone variables unchanged from the last input. Use this instead: while(myfile >> name >> phone) {...}
iostream status is not predictive. It reports the results of the
previous operation, not of the next (which would be impossible to
implement anyway). And myfile.good() isn't even a reliable indicator
of the results of the previous operation: if the operation failed, it
will be false, but if the operation succeeded, it's not necessarily
true. Forget that function.
Use the fact that the stream can act as a boolean to test success of the
previous operation. Once you have failure, you can then use eof(),
and bad() to determine why (but with regards to eof(), only after
failure---eof() may return true even if the previous operation
succeeded). Thus:
while ( myfile >> name >> phone ) {
// ...
}
if ( myfile.bad() ) {
std::cerr << "Hardware read error" << std::endl;
} else if ( !myfile.eof() ) {
std::cerr << "Format error in file" << std::endl;
}