0

I have good input files that look like this:

734 220 915 927 384 349 79 378 593 46 2 581 500 518 556 771 697
571 891 181 537 455 

and bad input files that look like this:

819 135 915 927 384 349 79 378 593 46 2 581 500 518 556 771 697
551 425 815 978 626 207 931 ABCDEFG 358 16 875 936 899 885 195 565
571 891 181 537 110 

where there is a space following the last integer at the end of both files. I'm trying to write a script in C++ that will read in all the integers unless there is a char/string as in the second example in which case it would alert me of this. I tried to write it like this:

int main()
{
int n;
bool badfile = false;
ifstream filein("data.txt");

while (!filein.eof())
{
    filein >> n;    
    if(filein.fail())
    {
        cout << "Not an integer." << endl;
        badfile = true;
        break;  
    }   
    cout << n << " ";   
}

cout << endl << "file check: " << badfile << endl;
}

but filein.fail() is triggered by the space at the end of a good file as well as a char/string in a bad file. So how can I set this up so that it ignores white spaces? Why does it only fail if there's a space at the end instead of either failing at all spaces or ignoring them altogether?

Austin
  • 5,355
  • 4
  • 40
  • 106

2 Answers2

1

The main issue is how you test for eof() on a stream... it's only set after an input attempt tries to read more characters when already at the end of file. Using std::ws first to consume whitespace means that eof detection can be reliable: if you're not then at eof() you know you're at some non-whitespace input that should be a number - if not you have an error in the input content.

Suggested code:

#include <iostream>
#include <fstream>
#include <iomanip>

int main()
{
    if (ifstream filein("data.txt"))
    {
        while (filein >> std::ws && !filein.eof())
        {
            int n;
            if (filein >> n)
                cout << n << ' ';
            else
            {
                std::cerr << "error in input\n";
                exit(EXIT_FAILURE);
            }
        }
        std::cout << '\n';
    }
    else
        std::cerr << "unable to open data.txt\n";
}

An alternative appear below, which might be easier to understand but isn't totally reliable. The problem is that you can reach EOF despite bad input such as a trailing - or +, as that'd be consumed while trying to read a number but isn't in itself enough to constitute successful parsing of a number. Only if the file is known to have a '\n' terminating the last line, will this be reliable:

        int n;
        while (filein >> n)
            cout << n << " ";   
        filein.clear();  // remove the error state
        if (filein.peek() != istream::traits_type::eof())
        {
            // while didn't reach EOF; must be parsing error
            std::error << "invalid input\n";
            exit(EXIT_FAILURE);
        }
Tony Delroy
  • 94,554
  • 11
  • 158
  • 229
  • Hmm.. this works, but I don't really understand how since it uses things from the std that are more advanced than what I've learned so far. Is there a more basic way to do it? Also I'm still confused as to why it causes a problem in the first place. I'm writing this more as a learning exercise as a beginner in fileIO rather than just trying to find a quick fix to make it work. – Austin Jul 21 '15 at 03:31
  • 1
    @AustinMW: there's more details of why `while (!in.eof())` is broken [here](http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong). There is another approach which you may or may not find simpler, I'll add it for your consideration. Cheers. – Tony Delroy Jul 21 '15 at 03:34
0

I'd suggest

ifstream filein("data.txt");
while (filein >> n)
    cout << n << " ";
if (filein.fail()) {
    cout << "Not an integer." << endl;
    badfile = true;
}
cout << endl << boolalpha << badfile << endl;
Shreevardhan
  • 10,417
  • 3
  • 32
  • 44
  • This returns "Not an integer." and badfile == true for the good data file. Thought there would be an easy way similar to this, but I guess not. Good to know the `boolalpha` though, haven't seen that yet. – Austin Jul 21 '15 at 04:16