Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::getline is reading line where specified delimiter is not present?

Tags:

c++

ifstream

I want to print each object in console from the array of the following string (stored in a file):

{ beforechars [{Object1},{Object2},{Object3}] afterchars }

I'm doing it as follows:

std::ifstream is("content.txt");
std::getline(is, content, '[');
while (std::getline(is,content,'{')) {
    std::getline(is,content,'}');
    std::cout << content << std::endl;
}
in.close();

But i am getting this output:

 Object1
 Object2
 Object3
] afterchars }

My understanding is that after Object3 iteration, the ifstream should have "}] afterchars }" and the while's guard shouldn't be true because there isn't any '{' char... Am i right? Where is the mistake?

like image 547
jscherman Avatar asked Mar 13 '23 01:03

jscherman


1 Answers

The while condition doesn't work as you expect: getline() will read successfully until it reaches an '{' or to the end of the file if not.

So what happens here ?

  • when you've displayed Object3 your position in the stream is after the closing '}'.
  • The getline() in the while condition will read all the remaining of the file into content as it encounters no '{'. As it could read something successfully, the condition is evaluated to true.
  • the getline() within the while block then fails to read anything, so content will remain unchanged. The stream is then in fail status. No subsequent operation will succeed until you clear this state. But nothing visible happens for now in your code.
  • after displaying this last result, the next loop condition will fail.

Simple workaround:

A very easy workaround would be to keep the current position in the stream before looking for '{', and in case it was not found, go back to this position. Attention: this way of parsing files is not so nice from point of view of performance, but it's ok for small files.

std::getline(is, content, '[');
auto pos = is.tellg();    // read current position
while (std::getline(is,content,'{') && !is.eof()) { 
    std::getline(is,content,'}');
    pos = is.tellg();     // update position before iterating again
    std::cout << content << std::endl;
}
is.seekg(pos);   // and get back to last position

The trick here is that if '{' is not found, after the getline() the stream is not yet in fail state, but eof() is already true. We can then end the loop and go back to the last recorded position.

Online demo

like image 93
Christophe Avatar answered Apr 29 '23 06:04

Christophe