File stream with repeating input - c++

I'm attempting to create a repeating menu that will allow a user to re-enter a file name if the program is unable to open the file.
Right now it works correctly if I enter the name of an existing file, but if the file doesn't exist it prints the "File not found" then executes the rest of the program. I'm new to file streams and most of the code here was found through references. I'm a bit lost on what exactly is going on and what the best way to handle the situation is. Any guidance would be appreciated.
typedef istream_iterator<char> istream_iterator;
string fileName;
ifstream file;
do {
cout << "Please enter the name of the input file:" << endl;
cin >> fileName;
ifstream file(fileName.c_str());
if (!file) {
cout << "File not found" << endl;
}
} while (!file);
std::copy(istream_iterator(file), istream_iterator(), back_inserter(codeInput));

After constructing the object file will always exist, so your loop condition always fails. Change the condition to whether the file didn't open properly.
do {
...
}
while (!file.is_open())

this code will work.
do {
std::cout << "Please enter the name of the input file:" << std::endl;
std::cin >> fileName;
file = std::ifstream(fileName.c_str());
if (!file) {
std::cout << "File not found" << std::endl;
}
} while (!file);
your error was that you have 2 definition of the file variable.
the variable in while (!file) that is used is the one defined outside the do-while loop, and it is valid state is set to true by default.

In addition to #acraig5075 answer:
Writing a type then a variable name (ifstream file) is to create a new variable. Obviously you know this, but if you use the same name again in, for example, a loop, it makes a new and distinct variable.
ifstream file; // a unique variable
...
do {
...
ifstream file(fileName.c_str()); // another unique variable
...so change the usage inside the loop to:
file.open(fileName.c_str());

Related

Opening a C++ .txt file using user-input

I'm trying to open a C++ .txt file as shown in my code below. This is part of a larger program that I'm working on where I write the contents of one file into another so that it contains the same information as the original but I am required to provide user-input. If the user-provides a .txt file that is not the one we are using, I have to produce an error message and prompt them to re-enter an input until they input the correct one (test.txt):
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
/* Refers to test.txt */
ofstream mainfile;
std::string filename;
std::cout << "Please enter the name of your data file: ";
std::cin >> filename;
mainfile.open(filename.c_str());
mainfile << "test.txt";
if(!mainfile) {
std::cout << "I'm sorry, I could not open '" << filename << "'." << std::endl;
std::cout << "Please enter another name: " <<
std::endl;
std::cin >> filename;
} else {
std::cout << "File '" << filename << "' opened successfully!" << std::endl;
}
return 0;
}
My current issue is that the program is terminating too early, even when I input incorrect inputs such as jaguar.txt or flowers.txt, anything that isn't "test.txt". In fact, when I input just about any .txt file name it will output saying that it opened successfully.
It seems that what you want to do is open up 2 different files, where one is used as the file to copy from (test.txt), and the other is the file to copy to (jaguar.txt). Instead of checking if test.txt exists with std::ofstream, you should instead use std::ifstream.
Using ifstream, if the file does not exist, your code will work properly. Instead, because you are currently using ofstream, the file will open correctly, because you're essentially telling it to make the file for you.
So basically, where you have used ofstream mainfile, instead it should be:
ifstream mainfile;
Later in the code, you can prompt the user for the file to copy to (i.e. jaguar.txt), and this will be the one where you output data using ofstream.
Use ifstream to read from a file, and ofstream to write to a file.
To check whether the source file exists, test the corresponding ifstream after trying to open it:
ifstream mainfile; // ifstream stands for "input file stream"
std::cout << "Please enter the name of your data file: ";
std::cin >> filename;
mainfile.open(filename);
while (!mainfile) { // asking endlessly, until the user inputs a good file
std::cout << "I'm sorry, I could not open '" << filename << "'." << std::endl;
std::cout << "Please enter another name: " <<
std::endl;
std::cin >> filename;
mainfile.open(filename);
}
std::cout << "File '" << filename << "' opened successfully!" << std::endl;
By the way, in the error message "open" is programmer's jargon. It's a general word which includes both reading and writing. If your application copies stuff from one file to the other, the user may get confused: is there a problem with input or output? You might want to say "read" instead of "open", even though technically you didn't read anything yet. That would make a clearer error message.
If you want to copy one file to another, use one of the methods described in a dedicated question.

Editing a text file if searched value is present

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.

Input validation of opening a text file in C++

I am building an input validation function that takes the input of the user and tries to open that file. and repeats if user is not entering the correct format. the correct format is:
test1.txt
My function works if I write correct format in the first run, but after the second run it keeps printing the error message although I am writing the write format to be opened. I have tried to clear the input "cin" and "filename" after taking the input but it did not work. Any ideas ?
string getFileInput()
{
string filename;
fstream file;
cout << "Please enter the name of the file: ";
getline(cin, filename);
file.open(filename.c_str());
while(!file.is_open())
{
file.clear(); file.ignore();
cout << "File name is incorrect, please enter again: ";
cin.clear(); cin.ignore();
getline(cin, filename);
file.open(filename.c_str());
}
// Extra condition. Empty file
if (file.eof())
{
cout << filename << " is an empty file." << endl;
}
file.close();
return filename;
}
I could reproduce and fix.
The problem is caused by cin.ignore(). According to cppreference:
... the next available character c in the input sequence is delim
So ignore will read the next line, up to the newline, and leave that newline alone. And the following getline can only read an empty string!
By the way, using a non opened fstream (file) for ignore and clear is at least useless and could be harmlfull because those methods are expected to be called on an open stream. And using cin.clear() is useless too and can be harmfull: if for any reason you have a read error (because you reached an end of file for example), you will consistently clear the error condition and try to read again when you should abort.
Finally, the eof condition is only set after a read returned nothing because of the end of file. It is never set when opening an empty file, nor if you could successfully read up to the end of file.
So the function should boil down to:
string getFileInput()
{
string filename;
fstream file;
cout << "Please enter the name of the file: ";
getline(cin, filename);
if (! cin) {
// test the error immediately and before using filename!
cerr << "read error: aborting...\n";
return "";
}
file.open(filename.c_str());
while(!file.is_open())
{
cout << "File name is incorrect, please enter again: ";
getline(cin, filename);
if (! cin) {
// test the error immediately and before using filename!
cerr << "read error: aborting...\n";
return "";
}
file.open(filename.c_str());
}
file.close();
return filename;
}

ifstream is.open() acting like it's not reading the file

//Prompts user for a file name and stores it
string fileName;
cout << "Enter the file name: ";
cin >> fileName;
ifstream inFile (fileName);
inFile.open(fileName);
//Prompt the user until they give the name of a file that can be opened
bool validFileName = false;
while(validFileName == false)
{
if(inFile.is_open())
{
validFileName = true;
}
else
{
cout << "Please enter a valid file name: ";
cin >> fileName;
ifstream inFile;
inFile.open(fileName);
}
}
//this block prints to the terminal, so it's opening
if(inFile.is_open())
{ cout << "It works! \n"; }
I am trying to create a program that will work with a file, but there needs to be a section that checks to see if the file that the user types in is an actual file that the program can open. I've tried a few different ways to write the while loop, because it needs to keep asking until it receives a valid file. I have the valid file name "input.txt", but even when I type that into the terminal it continues to print the error message. I have tried to type the file name with and without quotes, so I'm not sure what it is caught up on. I know it is opening the file, because I added a second check afterward and it appears that it's opening, so I think it's an issue with how I have the error check statement written?
The problem is that you are using
ifstream inFile;
inFile.open(fileName);
in the loop. The variable in the loop hides the variable of the same name outside the loop. Remove the first of those lines.
FWIW, you can simplify your code to:
ifstream inFile (fileName);
while(!inFile)
{
// Prompt the user until they give the name of a file that can be opened
cout << "Please enter a valid file name: ";
cin >> fileName;
inFile.open(fileName);
}
if(inFile)
{
cout << "It works! \n";
}
In:
ifstream inFile (fileName);
inFile.open(fileName);
The file is opened on the first line.
Reopening it is redundant.
Also in the loop you declare a temporary variable inFile which goes out of stop at the end of the else statement. Make sure to declare it only once at the outmost scope you would like to use it.

C++: How can I switch between input and output from file?

I'm trying to write up a program that will display the contents of a text file to the screen for a user. Specifically, the text file will be a list of names that the program will read and display each name to the user individually. The user will then either like the name and keep it or dislike the name and remove it.
My dilemma is: if the user elects to keep the name, the program will need to go from reading the file to "writing" (deleting the name) the file and then back to reading the file again! I found the following relevant code on http://www.tutorialspoint.com/cplusplus/cpp_files_streams.htm. It shows that one must use .close() to switch from reading to writing, but this seems funky to a newbie like me. Is there a better way to do it or is the code below just fine?
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
char data[100];
// open a file in write mode.
ofstream outfile;
outfile.open("afile.dat");
cout << "Writing to the file" << endl;
cout << "Enter your name: ";
cin.getline(data, 100);
// write inputted data into the file.
outfile << data << endl;
cout << "Enter your age: ";
cin >> data;
cin.ignore();
// again write inputted data into the file.
outfile << data << endl;
// close the opened file.
outfile.close();
This is where the file goes from write mode to read mode.
// open a file in read mode.
ifstream infile;
infile.open("afile.dat");
cout << "Reading from the file" << endl;
infile >> data;
// write the data at the screen.
cout << data << endl;
// again read the data from the file and display it.
infile >> data;
cout << data << endl;
// close the opened file.
infile.close();
return 0;
}
Also, I'm having a hard time finding how to read and modify individual characters in the file. I need to do this too, as the file needs to follow a specific pattern, with five names per line and one space between each name (newline at end of fifth name, obviously). Help with this would be appreciated.
Changing things mid-file is complicated.
What I would do is either create a temporary file, write the kept names to that file and replace the original file with this temporary file, (or just store the kept names in a vector and rewrite the file)
try
std::fstream ff("io.txt", std::fstream::in | std::fstream::out);
and fstream::read, fstream::write, fstream::seekg
If the file is small then load it into memory.
Otherwise use fopen with "a+" or "r+" mode and fseek.