Editing a text file if searched value is present - c++

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.

Related

Multiset: Problem with multiset adding more than one version of a word and cannot handle large amounts of text

Update and fixed: I have fixed the problem causing the error message- Huge thanks to user PaulMcKenzie for helping me understand what the error message was telling me!- When my program encountered a letter with a mark above it (diacritical marks I think they are called), it crashed. I have adjusted my code to account for these and now it doesn't crash at all! Another huge thanks to user ihavenoidea for helping me understand multisets! My program is now working the way it's supposed to!
Original post:
****I am VERY new to C++ so any and all help is appreciated!****
Ok, so I'm trying to use multiset to sort words so I can see how many times a word appears in a text. First, my program accepts a file, then it reads the words and takes out any punctuation, then it puts it into a multiset. After this, it is supposed to put the results into a text file the user names themselves.
My first issue is that the multiset seems to be creating more than one element for the same word (For example: in one of my tests I saw a(4) listed in the text document 3 times in a row instead of one time).
My Second issue is that when I try to read in large text documents (I'm using John Colliers story "Bottle Party" http://ciscohouston.com/docs/docs/greats/bottle_party.html to test it) my program completely crashes but doesn't crash when I test it with a smaller text document (small being with say about 5-10 lines of text). I'm using Visual Studios and (once again I'm new to Visual Studios also) I don't know what the error message is trying to tell me but it says:
After selecting retry:
As always, any and all help is greatly appreciated.
Code here:
#include <iostream>
#include <string> //for strings
#include <fstream> //for files
#include <set> //for use of multiset
using namespace std;
string cleanUpPunc(string);
//Global variables
multiset <string> words; //will change back to local variable later
int main() {
//Starting variables
string fileName1 = "", fileName2 = "", input = "", input2 = ""; //To hold the input file and the file we wish to print data to if desired
ifstream fileStream; //gets infor from file
//Program start
cout << "Welcome to Bags Program by Rachel Woods!" << endl;
cout << "Please enter the name of the file you wish to input data from: ";
getline(cin, fileName1);
//Trys to open file
try {
fileStream.open(fileName1);
if (!fileStream) {
cerr << "Unable to open file, please check file name and try again." << endl;
system("PAUSE");
exit(1);
}
while (fileStream >> input) {
input2 = cleanUpPunc(input); //sends the input word to check for punctation
words.insert(input2); //puts the 'cleaned up' word into the multiset for counting
}
fileStream.close();
//Sends it to a text document
cout << "Please name the file you would like to put the results into: ";
getline(cin, fileName2);
ofstream toFile; //writes info to a file
//Code to put info into text file
toFile.open(fileName2);
if (toFile.is_open()) {
multiset<string>::iterator pos;
for (pos = words.begin(); pos != words.end(); pos++) {
toFile << *pos << " " << words.count(*pos) << endl;
}
toFile.close();
cout << "Results written to file!" << endl;
}
else {
cout << "Could not create file, please try again." << endl;
}
}catch (exception e) {
cout << "Stop that. ";
cout << e.what();
}
cout << "Thanks for using this program!" << endl;
system("PAUSE");
return 0;
}
string cleanUpPunc(string maybe) {
//Takes out puncuation from string
//Variables
string takeOut = maybe;
//Method
for (int i = 0, len = maybe.size(); i < len; i++) {
if (ispunct(takeOut[i])) {
takeOut.erase(i--, 1);
len = takeOut.size();
}
}
return takeOut;
}

Text doesn't save into the file

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).

Combining three text files into one text file forming a sentence C++

I am trying to create a program in C++ using Microsoft Visual Studio 2013. The main purpose of this program is to read three different files(headlines1,headlines2, and headlines3) and put them all together to form a single file and creating a sentence within that output file. I have figured out a function that I could use, but this function only reads and prints the 3 files out onto the console window. When I try to change the cout statement into an outfile, my outfile that I created is blank... I don't know what to do or how to structure the code.
#include<fstream>
#include<iostream>
#include<cstdlib>
using namespace std;
void readingFile(string[], ifstream &); //Funtion Prototype
int main()
{
string header1[50], header2[50], header3[50]; //Declaring array with 50 elements
int size1, size2, size3;
ifstream Fin, Fin2, Fin3;
ofstream Fout;
Fin.open("Headlines1.txt"); //Reading from these 3 files.
Fin2.open("Headlines2.txt");
Fin3.open("Headlines3.txt");
if (!Fin || !Fin2 || !Fin3) //Checking for unsuccessful open
{
cout << "Input file opening failed.\n";
cin.ignore();
return 1;
}
Fout.open("testingHeadlines.txt"); //Used for unsuccessful opening output
if (!Fout)
{
cout << "Output file opening failed.\n";
cin.ignore();
return 2;
}
cout << "Building.... Editing....\n" << endl;
cin.ignore();
cout << "Headlines file 1 below:\n" << endl;
readingFile(header1, Fin); //Function call
cout << endl << endl;
cout << "Headlines file 2 below:\n" << endl;
readingFile(header2, Fin2); //Function call
cout << endl << endl;
cout << "Headlines file 3 below:\n" << endl;
readingFile(header3, Fin3); //Function call
cout << endl << endl;
cin.ignore();
return 0;
}
//the function 'readingFile'
//Pre-conditions: Reads the contents from the files of Headlines1,2,and 3
//Post-conditions: Prints out the contents.
void readingFile(string[], ifstream &infile)
{
char next;
infile.get(next);
while (!infile.eof()) //Reading until EndOfFile
{
cout << next; //Problem is here?? I would think.
infile.get(next);
}
}
I'm just not certain if where I said the "Problem is here??" is where the problem is at. Every time I change the cout to outfile(I know, I have to change the parameters within the function header) once doing that I open the outfile and the file is blank.
All the files contain random words/phrases and when put together they will make a sentence. For ex. Headlines1 contains '***Queen Jennifer*'** Headlines2 contains '***has brain surgery*'** Headlines3 contains '***after eating 30 jalapenos.*'** and When put together it should read 'Queen Jennifer has brain surgery after eating 30 jalapenos.' but the files contain more words/ phrases that what I just listed in my example.
When I run the program above I am able to read all three Headline files, but they are printed in up to down form. For example, my output on my console screen would be:
Queen
Jennifer
has brain surgery
after eating 30 jalapenos
Problem:
Getting my headlines to read from left to right.
Getting them into a output file instead of the console screen.
Help Please.
You could replace this...
void readingFile(string[], ifstream &infile)
{
char next;
infile.get(next);
while (!infile.eof()) //Reading until EndOfFile
{
cout << next; //Problem is here?? I would think.
infile.get(next);
}
}
...and the calls there-to, such as...
readingFile(header1, Fin);
...with this...
void readingFile(ifstream& infile, ofstream& fout)
{
char next;
while (infile.get(next)) //Reading until EndOfFile or error
if (next != '\n') // if not newline
fout << next; // stream to file
}
...and calls ala...
readingFile(Fin, Fout);
That way readingFile is told where to write the output, and filters out the newline characters that were causing the output to appear on different lines.
This should do the trick.
#include <fstream>
#include <string>
int main() {
// Open the three files.
std::ifstream file_1("Headlines1.txt");
std::ifstream file_2("Headlines2.txt");
std::ifstream file_3("Headlines3.txt");
// Combine into one string.
std::string content;
content += std::string(std::istreambuf_iterator<char>(file_1),
std::istreambuf_iterator<char>());
content += std::string(std::istreambuf_iterator<char>(file_2),
std::istreambuf_iterator<char>());
content += std::string(std::istreambuf_iterator<char>(file_3),
std::istreambuf_iterator<char>());
// Output the string into a single file.
std::ofstream output_file("testingHeadlines.txt");
output_file << content;
}
I'm not sure what you're wanting to do about spacing between files, but that shouldn't be too hard for you to fine-tune this code for.
one possible thing why your ouput is like this is that std::ifstream::get() that you used in infile.get(next) is a non-formatted reading method which means it will not skip white spaces and newline character \n by default. so you need to check if next value is a newline like this:
if(next == '\n') continue ;
before passing it to cout << next. Thus you will skip printing a newline in your console screen.

remove stop words from a text file

I'm still a beginner to programming, I got an assignment asks me to make a code that read a text file and then remove the stop words. I made a code but it's not that good. what i want is to know how to remove a word from a line and apply case folding to the file after removing the stop words.
here is my code...
string line, deleteline;
ifstream stopword;
stopword.open("example.txt");
if (stopword.is_open())
{
while ( getline (stopword,line) )
{
cout << line << endl;
}
stopword.close();
}
else cout << "Unable to open file";
ofstream temp;
temp.open("temp.txt");
cout << "Please input the stop-word you want to delete..\n ";
cin >> deleteline;
while (getline(stopword,line))
{
if (line != deleteline)
{
temp << line << endl;
}
}
temp.close();
stopword.close();
remove("example.txt");
rename("temp.txt","example.txt");
cout <<endl<<endl<<endl;
system("pause");
return 0;
}
The code would be right, but you closed the file... and after closing, you're trying to read from it later. That won't quite work, unless you open it again.
Here you close the file (7th line in main):
stopword.close();
And then, in the 2nd while you try to getline from that stream:
while (getline(stopword,line))
In order for that to work, you have to open the file again. And clear the stream, because it'll probably have the eofbit error state flag set.

Segfault during file read

The program is supposed to prompt the user for their username. Upon receiving the username it concatenates it with '.history' to create username.history. Then it opens that file (username.history) and reads the input from it. I am running into a segfault here though. Whenever it opens the file, which is empty because the file doesn't exist, it reads multiple lines and then throws the segfault. I think the problem might stem from how I'm trying to open the file, but I'm not sure. Here is the portion that is causing problems:
// File input and output
ifstream f_in;
ofstream f_out;
// Prompt user for their username.
char username[80];
cout << "Please input your username: " << endl;
cin >> username;
cout << endl;
cout << "Loading history file if it exists." << endl;
// Create file naem and initialize the file line counter to 0.
strcat(username, ".history");
int fcount = 0;
// Open file and read in lines if there are any.
// Place read lines into the command string for use later.
char tmp[50];
f_in.open(username);
while(!f_in.eof()){
f_in >> tmp;
cmd[fcount] = tmp;
fcount++;
}
f_in.close();
Other pertinent info:
cmd is declared as a global variable (char cmd[200][50])
Any help will be greatly appreaciated.
Not sure if it is the only issue, but cmd[fcount] = tmp is wrong. You should use strcpy().
while(f_in.good())
{
f_in >> tmp;
cmd[fcount] = tmp;
fcount++;
}