5

How to read a file twice (e.g. like an old two-pass assembler do) using std::ifstream?

I tried the obvious

#include <fstream>
#include <iostream>
#include <string>

int main(int argc, char**argv)
{
  std::string path = argc>1?std::string{argv[1]}:std::string(__FILE__);
  std::ifstream inp{path};
  int num=0;
  std::cout << "first pass" << std::endl;
  do {
    std::string lin;
    std::getline(inp,lin);
    if (inp.eof())
      break;
    num++;
    std::cout << "#" << num << ":" << lin << std::endl;
  } while (!inp.eof());
  inp.seekg(0, inp.beg);
  inp.sync();
  std::cout << "second pass" << std::endl;
  num=0;
  do {
    std::string lin;
    std::getline(inp,lin);
    if (inp.eof())
      break;
    num++;
    std::cout << "##" << num << ":" << lin << std::endl;
  } while (!inp.eof());
  inp.close();
  return 0;
}  

and it does not work (the second loop is looping indefinitely).

FWIW, compiling with GCC 4.9.2 on Linux/x86-64/Debian

precisions

Actually I am trying to parse a file made of lines like ** somename followed (in the next lines) by a JSON object, followed by some empty newlines (and repeatedly again perhaps a ** someothername followed by another JSON object etc...). I need a two-pass algorithm. The first pass is extracting all the names (like somename) and building some "empty" named things. The second pass is filling the named things from the JSON object following them. JSON parsing is done using a recent 1.5 jsoncpp library.

Basile Starynkevitch
  • 1
  • 16
  • 251
  • 479
  • 5
    clear bad flags may be ? `inp.clear();` – P0W Mar 06 '15 at 15:20
  • "second loop is looping indefinitely" and printing what?.. – Sergey Kalinichenko Mar 06 '15 at 15:27
  • 2
    I have this problem months ago, see my question-answer http://stackoverflow.com/questions/28331017/rewind-an-ifstream-object-after-hitting-the-end-of-file – Raydel Miranda Mar 06 '15 at 15:30
  • 2
    You should not use `ifstream::eof()` to control the reading loop. You should be using your file read operation to control the loop. that way once the read fails the loop stops. – NathanOliver Mar 06 '15 at 15:30
  • 1
    see also: http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong – sbabbi Mar 06 '15 at 16:35
  • What is the point of `while (!inp.eof());`? When will it ever hit? – Neil Kirk Mar 06 '15 at 16:49
  • Possible duplicate of [Rewind an ifstream object after hitting the end of file](https://stackoverflow.com/questions/28331017/rewind-an-ifstream-object-after-hitting-the-end-of-file) – Raydel Miranda Jan 15 '18 at 15:43

1 Answers1

3

The last call to std::getline upon reaching end-of-stream fails, setting failbit. Since failbit is set, the call to seekg has no effect. You need to clear the stream's status flags before calling seekg (DEMO):

namespace {
void one_pass(std::istream& is) {
  std::string lin;
  for (int num = 0; std::getline(is, lin); ++num) {
    std::cout << '#' << num << ':' << lin << '\n';
  }
}
} // unnamed namespace

int main(int argc, char**argv)
{
  std::ifstream inp{argc > 1 ? argv[1] : __FILE__};

  std::cout << "first pass\n";
  one_pass(inp);

  inp.clear();
  inp.seekg(0);

  std::cout << "\nsecond pass\n";
  one_pass(inp);
}
Casey
  • 38,510
  • 6
  • 83
  • 116