I am writing a program that I allow the user to specify an input file to open and when I test with incorrect file names, the program is behaving very weird and it seems to have something to do with the input buffer, but I don't know where to begin other than using getline() instead of cin >> but I have already tried that.
here is the code that I think may be the problem:
bool openfile(ifstream&);
string userInput();
int main()
{
// ...
while (!openfile(inputFile))
openfile(inputFile);
string input = userInput();
// ...
}
bool openfile(ifstream &inputFile)
{
string filename;
cout << "Please enter the name of the file or type quit to exit the program: ";
cin >> filename;
cout << endl;
if (filename == "quit")
exit(4);
else
inputFile.open(filename);
if (!inputFile)
{
cout << "The file \"" << filename << "\" could not be opened or does not exist.\n";
return false;
}
return true;
}
string userInput()
{
string englishSentence;
cout << "Please enter a sentence or type quit to exit the program: \n";
getline(cin, englishSentence);
if (englishSentence == "quit")
exit(4);
return englishSentence;
}
Those are the two functions that read any input. openfile() is called first as you can see. Any help is greatly appreciated. Let me know if you suspect something else in my code and I will paste it.
while (!openfile(inputFile))
openfile(inputFile);
What this does is attempt to open the file twice each iteration, as long as the first attempt fails. Also, you need to make sure that inputFile is closed before attempting to open it again since it appears that you're reusing the same file object repeatedly.
Certainly first try something like:
while (!openfile(inputFile))
;
You can just do:
while (!openfile(inputFile));
Since the way you have it it would request an input filename twice if it fails the first time.
Basically to outline the problem:
Begin Loop
Request Filename
Invalid Filename
Request Replacement Filename
Return to start of the loop (Checks again)
Some problems I see from your code:
int main(); { ... } does not define the main function. You need to drop the semicolon, or it won't even compile.
while (!openfile(inputFile)) openfile(inputFile); repeats openfile(inputFile) unnecessarily. If the first one (in the condition) fails and the second one (in the body) succeeds, a third call will be made (in the condition) to check if the loop should continue. What you probably want is just while (!openfile(inputFile)) { }.
You open a file in openfile and never use it in the subsequent userInput.
Related
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;
}
Hello I do a program and in my program I have a class Customer.
In order to save the customer on the computer I create a file and separate every data of customer with :: like name::password::phonenbr. But my problem is if I write the line that is in the comment on my code the data will be save into the file, but If I write the same line in the if() that checks if t the file is empty this doesn't do anything although that I see with the compiler that there is no problem with this line.
If you can help me it will be graceful !
void Shop::Add_Customer()
{
fstream myfile; myfile.open("CustomerFile.txt");
string name, password, phonenbr;
string buffer, delimitor = "::";
system("cls");
cout << "Name of the customer: "; cin >> name;
cout << "Password of the customer: "; cin >> password;
cout << "Phone number of the customer: "; cin >> phonenbr;
if (!myfile.is_open())
{
myfile.open("CustomerFile.txt", ios::out);
}
//myfile << name + delimitor + password + delimitor + phonenbr << endl;
if (myfile.peek() == std::ifstream::traits_type::eof())
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
}
else
{
while (getline(myfile, buffer))
{
if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
{
cout << "Customer already exist" << endl;
}
else
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
cout << "Customer insert in the file " << endl;
}
}
}
}
The EOF flag in the stream is set when any read of the stream fails because it tried to read past the end of the stream. Once EOF is set the stream is in a bad state and cannot be read or written until the EOF flag is cleared.
Here is a really simple example of what is going on:
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
cout << "file not open." << endl;
}
else
{
if (myfile.peek() == std::ifstream::traits_type::eof())
{
if (myfile.eof())
{
cout << "Need to clear the EOF flag." << endl;
}
}
}
}
Peeking at EOF set the EOF flag, putting the stream in an error condition and making it unwritable. Since we want to extend the file, we need to clear that flag with the aptly named clear method.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
cout << "file not open." << endl;
}
else
{
if (myfile.peek() == std::ifstream::traits_type::eof())
{
if (myfile.eof())
{
cout << "Need to clear the EOF flag." << endl;
}
myfile.clear();
if (!myfile.eof())
{
cout << "OK. EOF clear now." << endl;
}
}
}
}
Off topic stuff:
The following code
while (getline(myfile, buffer))
{
if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
{
cout << "Customer already exist" << endl;
}
else
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
cout << "Customer insert in the file " << endl;
}
}
will repeat for every line in the file, presumably checking the input customer against every customer in the file one by one. Every time the input customer does not match, the input customer will be added to the file. This means the input customer is likely to be added to the file several times. Worse, the program is reading and writing the same file at the same time and will probably wind up corrupting the file.
It would be better to read and compare and then if a match is not found advance to the end of the file and add the input customer.
In addition, the file open logic is needlessly complicated and may still fail
fstream myfile; myfile.open("CustomerFile.txt");
if (!myfile.is_open())
{
myfile.open("CustomerFile.txt", ios::out);
}
The first call to open will certainly fail if the file is not present, forcing the second call to open. Might as well just add the ios::outto this call and be done with it. The second call top open may fail for other reasons and is not tested for success, so I recommend
fstream myfile("CustomerFile.txt", ios::out);
if (!myfile.is_open())
{
perror("file not open: ");
}
else
{
// your code goes here
}
Documentation for perror
The root of your problem lies in your if-statement's condition:
(myfile.peek() == std::ifstream::traits_type::eof())
Apperantly, your file is open in fstream mode in the line:
fstream myfile; myfile.open("CustomerFile.txt");
Now the only reason I can get for why your if-statement's condition is not met is because the file modes are different. I'm not sure whether I am right or not (feedback is welcome in the comments box), but that's a reason I can come up with.
I tried one of my own methods that always works, and it worked for your code also. I replaced the following lines in your code:
if (myfile.peek() == std::ifstream::traits_type::eof())
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
}
With these lines:
myfile.seekg (0, ios::end);
int length = myfile.tellg();
if (length == 0)
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
}
The first line myfile.seekg (0, ios::end); gets the distance between the 2 points specified in the brackets. 0 and ios::end are self explanatory; 0 is the start of the file and ios::end is the end of the file.
The second line int length = myfile.tellg(); stores the value seeked in the above line in an int variable called length. The length is the number of characters the "cursor" would have to move to get from start to end of this file (try to imagine the cursor as the blinking thing similar to the one in Microsoft Word that is in front of the word you are typing, except here, you cannot see the cursor in your text file moving from start to end).
This if-condition is pretty straightforward. If the length is zero, meaning that the cursor has to move 0 points to get from the start of the file to the end of the file, then write whatever you want to that file. This technique worked (at least it did for me).
On a side note, there are a couple of other areas where your code can improve. For example, why have you added this if-statement:
if (!myfile.is_open())
{
myfile.open("CustomerFile.txt", ios::out);
}
This code is a repetition of these lines of your code:
fstream myfile; myfile.open("CustomerFile.txt");
The .open() command already fulfills the if-statement I pointed out. If the file specified in the open() is found, then it will open that file; else it will continue to create that new file. Therefore, that if-statement is redundant and should be removed as it consumes unnecessary CPU power and slows your program down (not by a lot, but you will soon realize that every millisecond counts in running your code; efficiency is key). I would recommend you to remove that if-statement.
Another issue is your 3 variables that you accept for input. Given that they are strings, why do you use the cin >> method? Using cin will only take the first word in your sentence; in your following line:
cout << "Name of the customer: "; cin >> name;
If you enter John Doe, it will only save John to the name variable, and it will move "Doe" to the next input variable, which is password in your case. If there is no other cin, then it will ignore the words after the space. Therefore, use the following line for all your input points:
getline(cin, name);
This function will get all the words and spaces as a single sentence till the point you hit Enter, unlike cin that will only get the first word and ignore the rest of the sentence.
Finally, your phone number should be of type int. I'll leave that for you to fix as per your requirement.
Hope I answered your question and hoped my tips were helpful. Good luck!
EDIT: Another point I missed about your code was that your while loop runs for every line. This means it will check for the particular customer's name at every single line of the file. This is not what you want. You want to read every line in the file, BUT if you find the customer, then you want to terminate the function without continuing for the next line. Also, you only want to print an error statement AFTER you have read the entire file, not just a single line.
else
{
int check = 0;
while (getline(myfile, buffer))
{
if (CheckIfCustomerExist(buffer, name, phonenbr) == true)
{
cout << "Customer already exist" << endl;
check = 1;
break;
}
}
if (check == 0)
{
myfile << name + delimitor + password + delimitor + phonenbr << endl;
cout << "Customer insert in the file " << endl;
}
}
What this piece of code does is that it runs through every line, checking for the customer in that line. If that line has the customer record, then it sets an check value of type int to 1 from 0 and the break statement terminates the while loop. After reading the entire file, it moves on to an if-statement. In this statement, if the check variable still has 0, it means that the file did not have the customer, in which the new record will be added to the file.
Also, I said that the phone_number should be an int value. I take that back as upon further though and input from fellow StackOverflow users, the phone number is better suited as a string value as its format may not be stored properly as an int value (for example, 0059875 will be stored as 59875).
I am writing a program that is to convert text that a user gives. I have tried this method by itself in a test program and it works perfectly; however when I try to implement it into the larger program, the user cannot give the program an input to store. The relevant code is as follows:
int main()
{
string str = "NULL";
int mappings = 0;
readMappings(mappings);
receiveInput(str);
displayInput(mappings, str);
return 0;
}
void readMappings(int &count)
{
ifstream readMappings; // Creates the function "readMappings"
string filename; // Generates the filename variable to search for mapping document
cout << "Enter the filename of the mapping document: ";
cin >> filename; // User enters filename
readMappings.open(filename); // "readMappings" function opens the given mappings document
while (!readMappings.is_open())
{
cout << "Unsble to open file. Please enter a valid filename: "; // If the user enters an invaled filename, the program will ask again
cin >> filename;
readMappings.open(filename);
}
if (readMappings.good()) // Mapping document is successfully opened
{
readMappings >> count; // Reads first line
}
readMappings.close(); // If everything fails in this function, the document will close
}
void receiveInput(string &input)
{
char correctness;
do {
cout << "\nPlease enter the text you would like to be converted to NATO:\n";
getline(cin, input);
cout << "You are about to convert: \"" << input << "\".\nIs this correct? (Y/N)" << endl;
cin >> correctness;
} while (correctness == 'N' || correctness =='n');
}
I thought it may have been the program waiting for another input from the user so I added a variable I assumed it would already fill, but it did not fix my solution. In my opinion, the problem is in the receiveInput function, but I could be wrong. Thanks in advance for any assistance.
Also, I am using function prototypes with correct reference variables.
I see two problems:
1) You're not checking for an EOF condition, after invoking std::getline().
2) You are mixing together both std::getline and the >> operator. Now, there's actually nothing technically wrong with that, but both std::getline and the >> operator have very nuanced semantics, when it comes to error checking and input consuming, that you need to get 100% right, in order to correctly use them together.
Just replace your usage of the >> operator with std::getline, so you're using std::getline exclusively, and make sure to check for fail() or eof(). You will find plenty of examples of how to correctly check for end of file and failure conditions, with std::getline, here on stackoverflow.com.
I am starting the first part of a school assignment and I must prompt the user to enter a filename, check for the existence of the file, and if it exists, open it for processing; otherwise I am to have the user enter another filename.
When I compile and run my program below, I get the error message "No file exists. Please enter another filename." When I type in names of files that don't exist it just runs the first part of my do while loop again. I'm a beginner at C++ but I've done this before and I feel as if it should be running properly. Any help would be appreciated.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct customerData
{
int _customerID;
string _firstName, _lastName;
double _payment1, _payment2, _payment3;
};
void processFile();
int main()
{
processFile();
system ("pause");
return 0;
}
void processFile()
{
string filename;
ifstream recordFile;
do
{
cout << "Please enter a filename\n";
cin >> filename;
recordFile.open(filename);
if (recordFile.good())
// {
// enter code for if file exists here
// }
;
}
while(recordFile.fail());
{
cout << "No file by that name. Please enter another filename\n";
cin >> filename;
recordFile.open(filename);
}
}
To check whether a file was successfully opened you must use the std::fstream::is_open() function, like so:
void processfile ()
{
string filename;
cout << "Please enter filename: ";
if (! (cin >> filename))
return;
ifstream file(filename.c_str());
if (!file.is_open())
{
cerr << "Cannot open file: " << filename << endl;
return;
}
// do something with open file
}
The member functions .good() and .fail() check for something else not whether the file was opened successfully.
I'm not 100% sure what your intent is here, but do you understand that you've only got one loop here? After your do/while loop, you've got some code in braces, but that's not connected to any loop construct... it's simply a new scope (which doesn't serve a purpose here).
So, your program does this:
1) Ask for filename. Try to open it. If file stream can be read, do the "enter code here" part.
2) Check if filestream is "bad". if so, go back to step 1. Otherwise, continue.
3) Print out "no file by that name", prompt for a new file, try to open it
That's almost certainly not what you want.
You can use c code.
FILE *fp = fopen("file" "r");
if(fp){
//do stuff
}
else{
//it doesnt exist
}
on a side note, when using namespace std try to make it not global
you can put it inside of your functions instead when necessary
int main(){
using namespace std;
//other std stuff
}
So i basically need my program to open a file and do something. When the program asks for the user to input the file name, and the user inputs the name of file correctly the first time, the operation works. But if the user typed the name wrong, the program says "invalid name try again" but then it is never able to open the file even if the user types the name correctly. Here's the code:
ifstream read_file(file.c_str());
while(true)
{
if(!(read_file.fail()))
{
...
}
else
{
cout << "Either the file doesn't exist or the formatting of the file is incorrect. Try again.\n>";
}
cin >> file;
ifstream read_file(file.c_str());
}
What is the problem, any thoughts? Thank you
You are redeclaring read_file inside the loop, but the code at the top of the loop always use the read_file outside the loop.
This is what you want instead:
ifstream read_file(file.c_str());
while(true)
{
if(!(read_file.fail()))
{
...
}
else
{
cout << "Either the file doesn't exist or the formatting of the file is incorrect. Try again.\n>";
}
cin >> file;
read_file.open(file.c_str()); /// <<< this has changed
}