views:

152

answers:

4

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!

+5  A: 
while(infile >> word)
{
    if(word == keyWord)
    {
        cout << word << endl;
        count++;
    }
}

This would do the job. Please read about streams more.

AraK
That runs the loop per word instead of per line. Not downvoting because I'm not sure if that's what the OP wants though...
Billy ONeal
Need to loop per word, but afterwards need to print out the line number that the word was located on.
Jeff
@Jeff: Ah. +1 then.
Billy ONeal
std::count using stream iterators will do the same thing :-)
Martin York
A: 

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.

Billy ONeal
This still fails, as the EOF is only true after you try and read past the end of file. Use: while( infile >> word) {}
Martin York
@Martin York: How so? We're both using `operator bool` so I fail to see how it fails :)
Billy ONeal
The problem is you will then perform a read inside the loop. Which may potentially fail because of EOF. So inside the loop you will do a read followed by another explicit test to see if EOF has been reached if it has then you will need to exit the loop before any processing is done. In the "standard pattern" you do the read as part of the while, thus if there is no more data the read cause the EOF to become true and thus the loop is never entered.
Martin York
You have to remember that the second to last read from a stream reads __UP-TO__ the EOF without triggering it. (So reading the last line of a file will read up-to the EOF (without triggering EOF). So there is no data left in the file BUT the loop will still be entered. But the first read that tries to read a file with no data will set the EOF flag inside the loop.
Martin York
@Marting York: Ah, I see now :) I'd still use std::getline but it does need to go in the loop, I agree.
Billy ONeal
Change your code above and I will delete the comments if you want.
Martin York
A: 

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.

Baltasarq
You should NOT check for EOF in the loop like that. It is not true until after you try and read past the end of the file. So if you have read the whole file EOF is still false so the loop is entered. Then the >> operator will fail attempting to read a word and the value in word is probably unspecified when you entered the if condition. Loop using this: while(infile >> word) {} The loop will only be entered if the >> operators succedes in reading a word from the file.
Martin York
Yep. It would be better to read before entering, and then again just before the en of the loop. The term is "reading ahead". But I didn't want to change so many things in his program.
Baltasarq
The trouble is that your example is the standard anti-pattern for reading a stream via a loop. Also it is incorrect as if the keyword appears on the last line it will potentially be counted twice (depending on whether >> operator clears word before testing the streams state or after).
Martin York
Okay, added the "reading ahead" technique.
Baltasarq
+1  A: 

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