Sail
Sail

Reputation: 31

The file was set up right but eof() and peek() return wrong value from new stream unexpectedly

I want to generate a random array.

void Random_nums(int n, string fileName) {

    ofstream fsave(fileName);
    int ranArray[n];

    srand( (unsigned int)time(NULL) );
    for(int i=0; i<n; i++)
    {
        ranArray[i]=rand()%2001 - 1000;
        fsave << ranArray[i] << '\n'; //save them into ranArray
    }

    fsave.close();
}

And I want to save them into a separate file.

After that I want to read the data I just saved and implement them in a new function.

void FunctionWantToUse(string inputFile, string outputFile) {

    //reading data
    ifstream fin(inputFile);
    cout << fin.is_open();//which is 1

    //how many numbers are there in this file
    int n = 0;
    n = distance(istream_iterator<int>(fin), istream_iterator<int>());

    //restore the array
    int array[n];

    //test
    cout << fin.peek() << ' ' << EOF;//they are both -1!

    //another test
    cout << fin.peek() << '\n'; //which return -1
    cout << fin.eof() << '\n'; //which return 1

    //since peek() and EOF both return -1, I can't get into this loop. 
    while (fin.peek() != EOF) {
        if ((fin >> array[i]).good()) {
            cout << array[i];
        }
    }

Completely have no idea why the value of fin.peek() and fin.eof() change to -1 and 1 respectively.

And the console told me this as I run these two functions.

-1 -1-1
1

Process finished with exit code 0

I also give some other tests, such as, place fsave.eof() in the loop when I generate random number, and it prints out all 0s.

Could someone please tell me what's going on here? I completely have no clue right now.

Upvotes: 3

Views: 922

Answers (1)

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 154025

With std::distance(istream_iterator<int>(fin), istream_iterator<int>()) you walk through the file until it fails to read and int. That an happen either because there is some input which cannot be parsed as int (after skipping leading whitespace) or end of file is reached. In any case, when the call to distance() returns the stream will have std::ios_base::failbit set and if the error is due to end of file being reach also std::ios_base::failbit.

The obvious fix is to not determine the distance and instead to read the integers directly into a suitable representation. For example:

std::vector<int> array{std::istream_iterator<int>(fin),
                       std::istream_iterator<int>()};

If you really want to use loop instead, you should test for the stream to be not in fail state after reading from the stream, e.g.:

for (int tmp; fin >> tmp; ) {
    // use tmp
}

When you really feel you need to determine the number of elements first, you'll need to

  1. clear() the stream state after it got std::ios_base::failbit set. Without that most stream operations will simply be ignored.
  2. seekg() back to the start of the stream.

Note, however, that resizing a std::vector<int> a few times will almost certainly be more effective than reading through a file and parsing its contents twice.

BTW, your use of array has two problems:

  1. Variable sized arrays are not part of standard C++ although some compilers do support them as an extension.
  2. If the file is of a reasonable size you'll get a stack overflow.

You are much better off using std::vector<int> instead.

Upvotes: 3

Related Questions