views:

182

answers:

6

I'm trying to complete an exercise to write a program that takes the following command line arguments: an input file, an output file, and an unspecified number of words. The program is to read the contents of the input file line by line, find for each word given which lines contain the word, and print the lines with their line number to the output file. Here's my code:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;

int main(int argc, char* argv[]) {
    if (argc < 4) {
        cerr << "Error #1: not enough arguments provided\n";
        return 1;
    }
    ifstream in(argv[1]);
    if (!in.is_open()) {
        cerr << "Error #2: input file could not be opened\n";
        return 2;
    }
    ofstream out(argv[2]);
    if (!out.is_open()) {
        cerr << "Error #3: output file could not be opened\n";
        return 3;
    }
    ostringstream oss;
    for (int i = 3; i < argc; ++i) {
        int k = 0;
        string temp;
        oss << argv[i] << ":\n\n";
        while (getline(in, temp)) {
            ++k;
            unsigned x = temp.find(argv[i]);
            if (x != string::npos)
                oss << "Line #" << k << ": " << temp << endl;
        }
    }
    string copy = oss.str();
    out << copy;
    in.close();
    out.close();
    return 0;
}

If I try to run that, I get the predicted output for the first word given, but any words following it aren't found. For example, for the source code above will give the following output:

in:

Line #1: #include <iostream>
Line #2: #include <fstream>
Line #3: #include <string>
Line #4: #include <sstream>
Line #5: using namespace std;
Line #7: int main(int argc, char* argv[]) {
Line #12:     ifstream in(argv[1]);
Line #13:     if (!in.is_open()) {
Line #14:         cerr << "Error #2: input file could not be opened\n";
Line #22:     ostringstream oss;
Line #23:     string temp;
Line #24:     for (int i = 3; i < argc; ++i) {
Line #26:         int k = 0;
Line #28:         while (getline(in, temp)) {
Line #30:             unsigned x = temp.find(argv[i]);
Line #31:             if (x != string::npos)
Line #32:                 oss << "Line #" << k << ": " << temp << endl;
Line #35:     string copy = oss.str();
Line #37:     in.close();
out:

That is, it'll find all the instances of the first word given but not any following. What am I doing wrong here?

EDIT: I had been trying to figure out a way to go back to the beginning of the file, but I sorta gave up after I couldn't find a method called "rewind()" or something like that. I added in.seekg(0, ios::beg) after the while loop, and it still gives me the same wrong output.

EDIT 2: Okay, I finally gave in and realized I wouldn't be able to get my original desired output without some sort of wild attempt at using vectors of pairs, so I gave in and decided to make it print in this form:

found in at line #31:         cerr << "Error #2: input file could not be opened\n";
found out at line #34:     ofstream out(argv[2]);

That is, it prints all the lines in order and heads each one with the specific word found. Here's the while-loop:

ostringstream oss;
string temp;
while(getline(in,temp)) {
    static int count = 1;
    for (int i = 3; i < argc; ++i) {
        unsigned foundWord = temp.find(argv[i]);
        if (foundWord != string::npos)
            oss << "found " << argv[i] << " at line #" << count << ": " << temp << endl;
    }
    ++count;
}

Anyway, thanks for all the help! The exercise itself didn't say the output had to be formatted in any way, so I consider it fully completed.

+5  A: 

At i=3, the file is read to EOF in the inner while(getline()) loop. So at i=4,5,6… the file is no longer available for reading.

You either need to rewind the input file back to the beginning after the inner loop ends, or switch the loop order (while(getline(...)) { ... for (int i = 3; ...) { ... } ... })

KennyTM
A: 

My guess is that you need to reset your file pointer to the beginning of the input file each time through your for loop. Right now, you open it once outside the loop and have no mechanic to reset it back to the beginning for following words.

Michael Dorgan
+3  A: 

You are looping through the strings you are looking for, but you have the getline() that reads the file in the loop. You really want:

for each input line
   getline
    for each string we are looking for
       is string in line?
anon
Thanks, this one really gave me an idea of what was needed.
Maulrus
+1  A: 

You can use the function in.seekg (0, ios::beg); to rewind to the beginning of the file before reading it again.

Mike Webb
A: 

To rewind the stream you first need to call in.clear() to unset the state flags of the stream. Only after doing this can you call in.seekg(0, ios::beg) to move the get position back to the beginning.

Mike Dinsdale
A: 

As @Neils answer suggests, your algorithm is flawed. His pseudo-code is a good starting-point for you. I suggest you take it and try to write each step as pseudo-code a bit more detailed. Do that repeatedly, until you feel that the pseudo-code easily translates into real code. I find this approach very useful, also for the more complex problems you may encounter later. Sarcastically speaking, think before you code.

Hint You should consider storing the words you are looking for in some data-structure, so that you can repeatedly iterate through them.

Space_C0wb0y
Thanks, I guess I was looking for the easy way out, but it would definitely be better to do it right. Though isn't keeping the words in argv[3] and beyond putting them in a data structure?
Maulrus