-2

I have made the following script, that is supposed to read from a file:

    char match[] = "match";

    int a;
    int b;

    inp >> lin;
    while(!inp.eof()) {
        if(!strcmp(lin, match)) {
            inp >> a >> b;
            cout << a << " " << b <<endl;
        }
        inp >> lin;
    }

    inp.close();
    return num_atm;
}

It is supposed to read all words, and if a line starts with match, it should then also print the rest of the line.

My input file is this:

match 1 2 //1
match 5 2 //2
nope 3 6 //3
match 5 //4
match 1 4 //5
match 5 9 //6

It will correctly print 1 2, 5 2, and skip 3 6. But then, it will get stuck and keep printing 5 0 and continue printing 5 0 for ever. I get that match is put into b, which is an integer, but I don't get why this is looped. Shouldn't the input read match 4 once, try to read/write 5 and match, and then be done with line 4 and the match from line 5? Then it should next read the number 1 and 4 and then match from number 6.

I would also understand that due to the word not fitting into the integer, it would read match in the fifth line again, but that's not what it does.

It goes back to the match in the fourth line which it already read, and reads it again. Why is this?

  • `eof` within loop: Have a look at [here](https://stackoverflow.com/q/5605125/1312382) (more important aspect: you do not catch fail in given case). – Aconcagua Nov 13 '18 at 19:11
  • Stepping through the code with a debugger should solve this for you in short order. – user4581301 Nov 13 '18 at 19:11
  • Simplify your life and use `std::string` and `std::getline`. – Thomas Matthews Nov 13 '18 at 19:15
  • @Aconcagua I see! I know how to fix it, but I don't understand why data gets re-read when fail is set to true. – PenguinHook Nov 13 '18 at 19:19
  • `lin` is not defined – M.M Nov 14 '18 at 00:16
  • @PenguinHook Data does not get re-read: Once the fail bit is set, the stream does not read anything at all any more and leaves the variable you pass to `operator>>` *unmodified*. That's why you see the same input again. If you want to be able to read again, you need to 1. [clear](https://en.cppreference.com/w/cpp/io/basic_ios/clear) the fail bit 2. handle the input that caused failure (because it is still there!). On std::cin, you'd typically ignore the yet buffered input (see e. g. [here](https://stackoverflow.com/a/25020223/1312382); there was a better one, but couldn't find it now). – Aconcagua Nov 14 '18 at 01:30

1 Answers1

0

When you are reading with >> line enndings are handled the same as spaces: They are just more whitespace that is skipped. That means you see

match 1 2 
match 5 2 
nope 3 6 
match 5 
match 1 4 
match 5 9 

But the program sees

match 1 2 match 5 2 nope 3 6 match 5 match 1 4 match 5 9 

Let's fast forward to where things go south

Contents of stream:

nope 3 6 match 5 match 1 4 match 5 9 

Processing

inp >> lin; // reads nope stream: 3 6 match 5 match 1 4 match 5 9 
if(!strcmp(lin, match)) { // nope != match skip body
}
inp >> lin; // reads 3 stream: 6 match 5 match 1 4 match 5 9 
if(!strcmp(lin, match)) { // 3 != match skip body
}
inp >> lin; // reads 6 stream: match 5 match 1 4 match 5 9 
if(!strcmp(lin, match)) { // 6 != match skip body
}
inp >> lin; // reads match stream: 5 match 1 4 match 5 9 
if(!strcmp(lin, match)) { // match != match Enter body
    inp >> a >> b; // reads 5 and fails to parse match into an integer.
                   // stream: match 1 4 match 5 9 
                   // stream now in failure state
    cout << a << " " << b <<endl; // prints 5 and garbage because b was not read

}
inp >> lin; // reads nothing. Stream failed
if(!strcmp(lin, match)) { // match != match Enter body
    inp >> a >> b; // reads nothing. Stream failed
                   // stream: match 1 4 match 5 9 
                   // stream now in failure state
    cout << a << " " << b <<endl; // prints g and garbage because b was not read

}

Because nothing is ever read, while(!inp.eof()) is utterly worthless. The end of the file can never be reached. The program will loop forever, probably printing whatever it last read. Successfully read.

Fixing this depends entirely on what you want to do if you have a match line without 2 numbers on it, but a typical framework looks something like

std::string line;
while(std::getline(inp, line) // get a whole line. Exit if line can't be read for any reason.
{
    std::istringstream strm(line);
    std::string lin;
    if(strm >> lin && lin == match) // enters if lin was read and lin ==  match
                                    // if lin can't be read, it doesn't matter. 
                                    // strm is disposable
    {
        int a;
        int b;

        if (strm >> a >> b) // enters if both a and b were read
        {
            cout << a << " " << b <<"\n"; // endl flushes. Very expensive. just use a newline.
        }
    }
}

Output from this should be something like

1 2 
5 2 
1 4 
5 9 

If you want to make some use of match 5... Well it's up to you what you want to put in b if there is no b in the file.

user4581301
  • 33,082
  • 7
  • 33
  • 54