How do you search a document for a string in c++? - c++

Here's my code so far:
#include<iostream>
#include<string>
#include<fstream>
using namespace std;
int main()
{
int count = 0;
string fileName;
string keyWord;
string word;
cout << "Please make sure the document is in the same file as the program, thank you!"
<< endl << "Please input document name: " ;
getline(cin, fileName);
cout << endl;
cout << "Please input the word you'd like to search for: " << endl;
cin >> keyWord;
cout << endl;
ifstream infile(fileName.c_str());
while(infile.is_open())
{
getline(cin,word);
if(word == keyWord)
{
cout << word << endl;
count++;
}
if(infile.eof())
{
infile.close();
}
}
cout << count;
}
I'm not sure how to go to the next word, currently this infinite loops...any recommendation?
Also...how do I tell it to print out the line that that word was on?
Thanks in advance!

while(infile >> word)
{
if(word == keyWord)
{
cout << word << endl;
count++;
}
}
This would do the job. Please read about streams more.

If all you want to do is count the number of keywords in a file then:
int count = std::count(std::istream_iterator<std::string>(infile),
std::istream_iterator<std::string>(),
keyword);
If you want to read words.
But also want to print the line numbers then somthing like this should work:
std::string line;
std::ifstream infile("plop");
int lineNumber = 0;
while(std::getline(infile, line))
{
++lineNumber ;
std::stringstream linestream(line);
int hits = std::count(std::istream_iterator<std::string>(linestream),
std::istream_iterator<std::string>(),
keyword);
if (hits != 0)
{
std::cout << "Line: " << lineNumber << " Matches(" << hits << ")\n";
}
count += hits;
}

The problem comes in this part of the source code:
getline(cin,word);
if(word == keyWord)
{
cout << word << endl;
count++;
}
First of all, you don't want to read lines from cin. You want to read words from infile. So you should replace the first line of your code inside the loop by:
infile >> word;
if(word == keyWord)
{
cout << word << endl;
count++;
}
Also, you should change the condition of the loop. You don't need to check if the infile is open here. You should check for that before the loop starts. For the loop, you need to check whether the eof state has been reached or not:
if ( !infile.is_open() ) {
cerr << "Error while opening file." << endl;
exit( EXIT_FAILURE );
}
while( !infile.eof() ) {
infile >> word;
if(word == keyWord)
{
cout << word << endl;
count++;
}
}
And as you can see, now you can get rid off that strange second if you put inside the loop.
Last step is to introduce the "reading ahead" technique: it does not make sense to test for eof when we haven't read anything.
if ( !infile.is_open() ) {
cerr << "Error while opening file." << endl;
exit( EXIT_FAILURE );
}
infile >> word;
while( !infile.eof() ) {
if( word == keyWord )
{
cout << word << endl;
count++;
}
infile >> word;
}
Hope this helps.

Change while(infile.is_open()) to while(infile). Then you can remove the redundant eof test at the end.
It's still open even if you've encountered an error or reached the end of file. It's likely you are in a scenario where failbit is getting set (getline returns nothing), but eof has not been encountered, so the file never gets closed, so your loop never exits. Using the operator bool of the stream gets around all these problems for you.

Related

Writing to txt file C++

I would like to write the words in the file until I type the word "stop", but only the first word is saved to the file.
What's the problem?
int main(int i)
{
ofstream file;
string file_name,message;
cout << "\nFilename: ";
cin >> file_name;
cout << "Write 'stop' to end writig to file" << endl;
for(i=0; message!="stop"; i++)
{
cout << "\nYour message: ";
cin >> message;
file.open(file_name.c_str());
file << message.c_str() << "\t" ;
}
file.close();
return 0;
}
It should be,
int main()
{
int i;
ofstream file;
string file_name,message;
cout << "\nFilename: ";
cin >> file_name;
cout << "Write 'stop' to end writig to file" << endl;
file.open(file_name.c_str());
for(i=0; message!="stop"; i++)
{
cout << "\nYour message: ";
cin >> message;
if(message == "stop"){ //If you dont want word stop
break;
}
file << message.c_str() << "\t" ;
}
file.close();
return 0;
}
It would be better if you do something like,
do{
//do stuff
if (message == "stop")
break;
}while(message != "stop");
In this case, you better switch to a while loop of the form: while (!file.eof()), or while (file.good()).
Apart from that, the for loop has to define the variable, in your case i is undefined, and must contain the range of the variable and no other variable definition (condition on message must not be inside it. It has to be an if condition inside the for loop).
...
char word[20]; // creates the buffer in which cin writes
while (file.good() ) {
cin >> word;
if (word == "stop") {
break;
...
}
}
...
Actually, I am not sure how it compiles at all in your case :) For future reference: for loop should look like this: for (int i = 0; i<100; i++) {};
I hope it is clear!

Exception thrown at 0x5914F3BE (ucrtbased.dll)

I have some code that takes a list of names + double values from a .txt file and displays these in the command prompt. For this an array of structs is dynamically allocated. The code should know the size of the array based on the first value in the .txt file, which is then followed by the names and associated values. It should then display the list in two parts with names that have an associated double value higher than or equal to 10.000 listed first. If none of the values qualifies, it displays 'None' in the first half.
The program executes, but the debugger gives an exception and the output is not as expected.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
struct donor
{
string name;
double contribution = 0;
};
int main()
{
string filename;
ifstream inFile;
cout << "Enter name of data file: ";
cin >> filename;
inFile.open(filename);
cin.clear();
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
cin.clear();
donor* dlist = new donor[amount];
int i;
while(inFile.good())
{
for(i = 0; i < amount; i++)
{
getline(inFile, dlist[i].name);
cin.clear();
inFile >> dlist[i].contribution;
cin.clear();
}
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons = false;
for(i = 0; i < amount; i++)
{
if(dlist[i].contribution >= 10000)
{
grandpatrons = true;
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
if(grandpatrons == false)
{
cout << "None" << endl;
}
cout << "Here's the list of Patrons:\n";
for (i = 0; 1 < amount; i++)
{
if (dlist[i].contribution < 10000)
{
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
delete[] dlist;
return 0;
}
The donorlist.txt file looks like this:
4
Bob
400
Alice
11000
But the output looks like this:
Enter name of data file: donorlist.txt
Here's the list of Grand Patrons:
None
Here's the list of Patrons:
0
0
0
0
The exception that the debugger gives me is:
Exception thrown at 0x5914F3BE (ucrtbased.dll) in 6_9.exe: 0xC0000005: Access violation reading location 0xA519E363.
Now I assume something is going wrong with reading from the dynamically allocated memory. Maybe something is causing me to read from memory beyond the allocated array? I'm having trouble finding exactly where the mistake is being made.
Your problems begin with the wrong amount written in your data file.
Fix it with:
2
Bob
400
Alice
11000
They then continue with the fact that you inccorectly read the file.
Remember: Mixing operator>> and getline() is not as simple as it seems.
You see, operator>> IGNORES newline and space characters until it finds any other character.
It then reads the upcoming characters until it encounters the next newline or space character, BUT DOES NOT DISCARD IT.
Here is where the problem with getline comes in. getline reads EVERYTHING until it encounters newline or a specified delim character.
Meaning, that if your operator>> stops after encountering newline, getline will read NOTHING since it immediately encounters newline.
To fix this, you need to dispose of the newline character.
You can do this by first checking if the next character in the stream is indeed newline and then using istream::ignore() on it;
int next_char = stream.peek();
if(next_char == '\n'){
stream.ignore();
}
A working example of your code would be:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Suggestion: class/struct names should start with a capital letter.
struct Donor{
//Suggestion: Use member initializer lists to specify default values.
Donor() : name(), contribution(0){}
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
//Suggestion: Open the file immediately with the filename and use `operator bool` to check if it opened.
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount; //! Leaves '\n'
Donor* donors = new Donor[amount];
for(int i = 0; i < amount; ++i){
switch(inFile.peek()){
case '\n': inFile.ignore();
break;
case EOF: cout << "Donor amount too big!\n";
exit(EXIT_FAILURE);
}
getline(inFile, donors[i].name);
inFile >> donors[i].contribution;
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons_exist = false;
for(int i = 0; i < amount; ++i){
if(donors[i].contribution >= 10000){
grandpatrons_exist = true;
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
if(!grandpatrons_exist){
cout << "None\n";
}
cout << "Here's the list of Patrons:\n";
for(int i = 0; 1 < amount; ++i){
if(donors[i].contribution < 10000){
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
delete[] donors;
return 0;
}
Now, an even better solution would be to use vectors instead of raw pointers and implement operator>> and operator<< which would greatly simplify
the reading and printing of the objects.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Donor{
public:
Donor() noexcept: name(), contribution(0){}
friend istream& operator>>(istream& stream, Donor& donor){
switch(stream.peek()){
case EOF: return stream;
case '\n': stream.ignore();
}
getline(stream, donor.name);
stream >> donor.contribution;
return stream;
}
friend ostream& operator<<(ostream& stream, const Donor& donor){
stream << donor.name << ' ' << donor.contribution;
return stream;
}
const string& get_name() const noexcept{
return name;
}
const double& get_contribution() const noexcept{
return contribution;
}
private:
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
vector<Donor> donors(amount);
//Read it as `for donor in donors`
for(Donor& donor : donors){
inFile >> donor;
}
//An STL function that takes a lambda as the thirs argument. You should read up on them if you haven't.
//I would prefer using this since it greatly improves readability.
//This isn't mandatory, your implementation of this part is good enough.
bool grandpatrons_exist = any_of(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(grandpatrons_exist){
for(const Donor& donor : donors){
if(donor.get_contribution() >= 10000){
cout << donor << '\n';
}
}
}
else{
cout << "None\n";
}
cout << "\nHere's the list of Patrons:\n";
for(const Donor& donor : donors){
if(donor.get_contribution() < 10000){
cout << donor << '\n';
}
}
return 0;
}
Some other great improvements would be:
Use partition to seperate great patrons from normal ones.
Use stream iterators to read the objects into the vector.
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
//Ignore the first line completely
inFile.ignore(numeric_limits<streamsize>::max(), '\n');
//Calls `operator>>` internally
vector<Donor> donors(istream_iterator<Donor>{inFile}, istream_iterator<Donor>{});
auto first_grand_patron = partition(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(first_grand_patron == begin(donors)){
cout << "None!\n";
}
for(auto patron = begin(donors); patron != first_grand_patron; ++patron){
cout << *patron << '\n';
}
cout << "\nHere's the list of Patrons:\n";
for(auto patron = first_grand_patron; patron != end(donors); ++patron){
cout << *patron << '\n';
}
return 0;
}
Now some general tips:
Struct/Class names should start with a capital letter.
Stop Using std::endl.
No need to cin.clear(). Cin is only used once and never again.
Use member-initializer lists.
Optionally use ++i instead of i++ in for loops to get used to the correct way of incrementing a variable unless needed otherwise.
bool grandpatrons is too much of an abstract name for a flag.
donors is a subjectively better name than short for donor list.

C++ How to Check what words aren't in 2 similar files

i was trying to find a way to check two different files and get, from the second, all lines that aren't in the first.. but does all the opposite.
I tried the possible to solve this but nothing...
This is the code:
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
char username[UNLEN+1];
DWORD username_len = UNLEN+1;
GetUserName(username, &username_len);
stringstream buffer;
buffer << "C:\\Users\\" << username << "\\Desktop\\";
stringstream buffer2;
buffer2 << "C:\\Users\\" << username << "\\Desktop\\Legit.txt";
stringstream buffer3;
buffer3 << "C:\\Users\\" << username << "\\Desktop\\Unlegit.txt";
stringstream buffer4;
buffer4 << "C:\\Users\\" << username << "\\Desktop\\result.txt";
string results = buffer4.str();
int offset;
int num;
num = 1;
string search;
string linea;
string legit;
string unlegit;
string line;
cout << "Is the Legit.txt file at '" << buffer.str() << "'? [Y/N]: ";
cin >> legit;
if (legit == "Y" || legit == "y"){
}else if(legit == "N" || legit == "n"){
return 0;
}else{
cout << "\n.";
return 0;
}
string legitfile = buffer2.str();
cout << "\nIs the Unlegit.txt file at '" << buffer.str() << "'? [Y/N]: ";
cin >> unlegit;
if (unlegit == "Y" || unlegit == "y"){
}else if(unlegit == "N" || unlegit == "n"){
return 0;
}else{
cout << "\n";
return 0;
}
string unlegitfile = buffer3.str();
ifstream file(legitfile.c_str());
if(file.is_open()){
while(getline(file, line)){
ifstream MyFile(unlegitfile.c_str());
if(MyFile.is_open()){
while(!MyFile.eof()){
getline(MyFile,linea);
if((offset = linea.find(line, 0)) != string::npos) {
cout << "\n[" << num << "]" << " Word Found: " << line << "\n";
num++;
fstream result(results.c_str());
result << line << "\n";
result.close();
}
}
MyFile.close();
}
}
file.close();
return 0;
}else{
cout << "\nThe file '" << legitfile << "' does not exist.";
cout << "\nThe file '" << unlegitfile << "' does not exist.";
}
}
As i said, This code checks which words are equals in both (first & second) files and, once found, writes them to a third file, there is a way to do the opposite (check the two files and get the words that aren't equals)? Thank you so much!
I'm new, both in the forum and in C++, sorry if I make any mistakes. (sorry for my bad english too).
The classic solution to this sort of problem is to use a hash table collection to represent all the words in the first file. Then while iterating items from the second file, consult the set constructed of the first file. In C++, the std::unordered_set will do fine.
#include <unordered_set>
using namespace std;
unordered_set<string> firstFileSet;
unordered_set<string> missingFromSecondFileSet;
string line;
while(!firstfile.eof())
{
getline(firstfile,line);
firstFileSet.insert(line);
}
Then for each word in the second file, use a second set collection to keep track of what words are missing.
while(!secondfile.eof())
{
getline(secondfile,line);
if (firstFileSet.find(line) != firstFileSet.end())
{
missingFromSecondFileSet.insert(line);
}
else
{
firstFileSet.erase(line);
}
}
After the above runs, firstFileSet contains all the lines in the first file that were not present in the second. missingFromSecondFileSet contains all the lines in the second file that were not in the first:
for (auto &s : firstFileSet)
{
cout << s << " was in the first file, but not the second" << endl;
}
for (auto &s : missingFromSecondFileSet)
{
cout << s << " was in the second file, but not the first" << endl;
}
There is a program called diff on linux which does just what you are looking to do in C++.
It is written in C so you can just copy its source code =P
for (;; cmp->file[0].buffered = cmp->file[1].buffered = 0)
{
/* Read a buffer's worth from both files. */
for (f = 0; f < 2; f++)
if (0 <= cmp->file[f].desc)
file_block_read (&cmp->file[f],
buffer_size - cmp->file[f].buffered);
/* If the buffers differ, the files differ. */
if (cmp->file[0].buffered != cmp->file[1].buffered
|| memcmp (cmp->file[0].buffer,
cmp->file[1].buffer,
cmp->file[0].buffered))
{
changes = 1;
break;
}
/* If we reach end of file, the files are the same. */
if (cmp->file[0].buffered != buffer_size)
{
changes = 0;
break;
}
}
Taken from ftp://mirrors.kernel.org/gnu/diffutils/diffutils-3.0.tar.gz > src/analyze.c

Assign values to variables in c file by reading excel sheet

I need to assign values to variables in C file by reading an excel sheet. I have written a code but only the last variable has been assigned a value since I have used for loop.It is overwriting the values assigned to the previous variables as I am creating a different output file after assigning values.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string s1, s2, s3,s4;
string filename, text, line;
string cfilename,funcname, signal, value;
int i , k , m;
cout << "Enter excel filename" << endl;
cin >> filename;
cout << "How many lines of text are in the file?" << endl;
cin >> m;
fstream file(filename);
if (!file) {
cerr << "No such file exists." << endl;
exit(1);
}
if (file.is_open()) {
while (file.eof()==0){
for (k = 0; k < m; k++) { //Loops for as many lines as there are in the file
for (i = 0; i < 4; i++) { //Loops for each comma-separated word in the line
if (i == 0){
getline(file, text, ',');
cfilename=text;
cout << cfilename << '\t';}
else if (i == 1){
getline(file, text, ',');
funcname=text;
cout << funcname << '\t';}
else if (i == 2){
getline(file, text, ',');
signal=text;
cout << signal << '\t';}
else if (i == 3){
getline(file, text, '\n');
value=text;
cout << value << '\n';}
}
string s1=signal,s2=value;
s2 = s2 + "; //";
int offset, inset;
string line;
string search=s1;
fstream cfile(cfilename);
fstream fileOutput;
fileOutput.open("output.c");
if(cfile.is_open() && fileOutput.is_open()) {
while(!cfile.eof()) {
getline(cfile, line);
if ((offset = line.find(funcname)) != string::npos){
cout << "found: " << funcname << endl;
string line1;
fileOutput << line << '\n';
skip:
while(getline(cfile,line1)){
if((inset=line1.find(search, 0)) !=string::npos){
cout<<"found: " << search << endl;
string s3 = s1+ "=" +s2;
//cout<<s3;
line1.replace( inset, inset+s1.size(), s3 );}
fileOutput << line1 << '\n';
goto skip;
}
getchar(); }
fileOutput << line << '\n'; }
cfile.close();
fileOutput.close();
}
}
}
}
file.close();
getchar();
return 0;
}
I am trying to search a function first and then variables inside that function.
Need some help here.
I'm not sure it's this the problem but... I thing the while () is too much.
when you have readed the m lines of the file, your
while (file.eof()==0)
result true because you haven't read nothing past the end of file.
So you read other m lines (failing but without controls about the success of the reading).
EDIT: I think you should write something like
cout << "Enter excel filename" << endl;
cin >> filename;
fstream file(filename);
if (!file) {
cerr << "No such file exists." << endl;
exit(1);
}
while ( getline(file, cfilename, ',') && getline(file, funcname, ',')
&& getline(file, signal, ',') && getline(file, value, '\n')) {
cout << cfilename << '\t' << funcname << '\t' << signal << '\t'
<< value << '\n';
string s1=signal,s2=value;
[...]
The program does exactly what you asked it to do:
you read a CSV file (excel format is binary!) line by line
for each line in csv:
you erase the output file because you open a fstream without specifying ios::ate
search something in a source file and write to the output file.
As you erase the output file at every new line from the input CSV file, you cannot get more than the last operation.
It would be much simpler if you opened the output file once outside of the loop.
And... while (file.eof() == 0) is an anti-pattern. You look whether you have reached end of file before trying to read a line, and then read 4 values when the first getline have set the eof flag. You must test for eof immediately after a read and not before...

How to forbid the inclusion of non-numbers?

I have this program:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int i=0;
float reader, tot = 0;
while(i!=10)
{
cout << "Insert a number: " << endl;
cin >> reader;
if(cin)
{
tot += reader;
i++;
}
else
{
cout << "Error. Please retry." << endl;
cin.clear();
}
}
cout << "Media: " << tot/i << endl;
return 0;
}
In the IF() i want the user to insert ONLY FLOAT VALUES in "reader" variable.
I want that if the user inserts a number, the program continues... else the program should re-ask to the user to insert a correct value.
How to do this checking the INPUT? I tried with a TRY CATCH but it didn't work.
Thanks in advance!
"How to do this checking the INPUT?"
It's already ensured by
cin >> reader;
that only valid float values can be entered by the user. The way to check for validity is
if( cin >> reader ) {
tot += reader;
i++;
}
else {
cout << "Error. Please retry." << endl;
cin.clear();
std::string dummy;
cin >> dummy; // <<< Read invalid stuff up to next delimiter
}
Here's the fully working sample.
"I tried with a TRY CATCH but it didn't work."
To get exceptions from std::istream operations, set the std::istream::exceptions mask.
Just check the result of operator>>:
if (cin >> reader) {
// user gave you a float
tot += reader;
i++;
}
else {
cout << "Error. Please retry." << endl;
// clear the error flag on cin, and skip
// to the next newline.
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Your original program was fine except you need to skip over that bad input when you get an error. Simply clearing the error is not enough:
#include <iostream>
#include <string>
#include <limits> // include this!!!
using namespace std;
int main()
{
int i=0;
float reader, tot = 0.0f; // tot needs to be float
while(i!=10)
{
cout << "Insert a number: " << endl;
if( cin >> reader )
{
tot += reader;
i++;
}
else
{
cout << "Error. Please retry." << endl;
cin.clear();
// Then you ned to skip past the bad input so you
// don't keep tripping the same error
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
cout << "Media: " << tot/i << endl;
return 0;
}
The function cin.ignore() ignores as many input characters as possibly up until the end-of-line character '\n'. The std::numeric_limits<std::streamsize>::max() function tells us the maximum number possible characters that can be stored in an input buffer.
If it is confusing an alternate way to skip the bad input would be to simply read up to the next line into a std::string.
else
{
cout << "Error. Please retry." << endl;
cin.clear();
// Then you need to skip past the bad input so you
// don't keep tripping the same error
std::string skip;
std::getline(cin, skip); // read the bad input into a string
}