remove stop words from a text file - c++

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.

Related

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;
}

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

I Can't print out a file that I wrote on

I have created a function to write some data on a text file, and it works fine. I created another function to read in all the content of the file, and print it out for me! But, it is not working for some reason. Could any one help please?
This is my function:
void myClass::displayFile() {
char line[LINE]; //to hold the current line
file.open("data.txt", ios::app);
//keep reading information from the file while the file is open and has data
while (!file.fail() && !file.eof()) {
int lineSize; //to loope through each line
file.getline(line, LINE);
lineSize = strlen(line);
//loop through the line to print it without delimiters
for (int i = 0; i < lineSize; ++i) {
if (line[i] == ';') {
cout << " || ";
} else {
cout << line[i];
}
}
}
file.close();
file.clear();
if (file.fail()) {
cerr << "Something went wrong with the file!";
}
}
Note: The function compiles and the loop is accessible, but the line string is empty.
This is the writing function:
void myClass::fileWriter() {
file.open("data.txt", ios::app);
file << name << ";" << age << ";" << "\n";
file.close();
file.clear();
}
Silly me, the cause of your problem was staring me right in the face from the beginning, and it's the app open-mode that's the problem. It is to open the file in write mode, which means you can't read from it.
And even if you could read from the file, the cursor is placed ad the end of the file the eofbit flag would have been set inside the first iteration anyway.
If you want to read from a file, then either use std::ifstream which automatically sets the in mode if you don't specify a mode, or you have to explicitly set the in mode when opening.

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++;
}