2

I'm opening a file in a main function. I want to pass this file to a few functions but it looks like a first called function is clearing this file. This is a file:

1 5 6 8
6 5
1
2 6 8
6 7 5 1 2 4

First function counts lines in file. Second function counts the number of individual numbers.

int countTransactions(ifstream &dataBaseFile) {
    int numOfTransactions = 0;
    string line;

    while(getline(dataBaseFile, line))
        numOfTransactions++;

    cout << "countTransaction" << endl;
    cout << numOfTransactions << endl;
    return numOfTransactions;
}

void countItems(ifstream &dataBaseFile) {
    map<int, int> items;
    map<int, int>::iterator it;
    int item;

    while(!dataBaseFile.eof()) {
        dataBaseFile >> item;

        it = items.find(item);
        if(it != items.end()) {
            it->second++;
            continue;
        } else items.insert(make_pair(item, 1));
    }

    for(it = items.begin(); it != items.end(); it++)
        cout << it->first << " => " << it->second << endl;
}

int main() {
    ifstream dataBaseFile("database3.txt", ios::in);

    if(!dataBaseFile.good()) {
        cout << "Failure while opening file";
        exit(1);
    }

    countItems(dataBaseFile);
    countTransactions(dataBaseFile);

    dataBaseFile.close();
}

This is an output:

countTransation
5
countItems
P.Musiał
  • 81
  • 5
  • Relevant: [Why is iostream::eof inside a loop condition considered wrong?](https://stackoverflow.com/q/5605125/27678) – AndyG Jul 18 '17 at 13:16
  • Consider what happens to the stream after `countItems` completes. (Rather, what *has* happened by the time it completes) – AndyG Jul 18 '17 at 13:17
  • The stream has a position in the file. If you want to reread from the beginning you have to move this position (seekg). –  Jul 18 '17 at 13:18

1 Answers1

7

std::ifstream has a state, meaning that operations that you apply to it influence results of future operations. For example, streams have a reading position. When you read something from a stream, reading position advances by the amount of data you have read.

When you pass dataBaseFile to countItems, it reads the entire file, and advances the reading position all the way to the end. This is where the position remains when you call countTransactions, so it thinks there is nothing to read.

Resetting the read position back to zero will fix this problem:

countItems(dataBaseFile);
dataBaseFile.clear(); // To clear out EOF
dataBaseFile.seekg(0, ios::beg);
countTransactions(dataBaseFile);

However, this is not ideal in terms of performance, because you end up reading the file multiple times. When the file is small, you would be better off reading the entire file into memory, e.g. into std::vector<std::string>, and the using the in-memory representation for much faster access.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523