Reuse std::cin after eating an EOF

后端 未结 1 703
执念已碎
执念已碎 2021-01-14 21:53

The unix command wc has this functionality:

$ wc - - -
aaa bbb ccc
0 3 11 -
aaa bbb ccc
0 3 11 -
aaa bbb ccc
0          


        
相关标签:
1条回答
  • 2021-01-14 22:39

    With the patient help of Davis, I learned the difference between typed input finished with Ctrl+D and a here document which I was not aware of. (My tribute to Davis Herring.)

    Once, I got it the rest in C++ was quite simple like shown in my MCVE.

    line-count.cc:

    #include <fstream>
    #include <iostream>
    #include <string>
    
    unsigned process(const std::string &fileName, std::istream &in)
    {
      unsigned nLines = 0;
      if (in.bad()) {
        std::cerr << "ERROR: Cannot open '" << fileName << "'!\n";
        return 0;
      }
      for (std::string buffer; std::getline(in, buffer); ++nLines);
      std::cout << "File: '" << fileName << "', " << nLines << " counted.\n";
      return nLines;
    }
    
    int main(int argc, char **argv)
    {
      unsigned nLines = 0;
      for (int i = 1; i < argc; ++i) {
        const std::string arg = argv[i];
        if (arg == "-") {
          nLines += process(arg, std::cin);
          std::cin.clear();
        } else {
          std::ifstream fIn(arg.c_str());
          nLines += process(arg, fIn);
        }
      }
      std::cout << "Total: " << nLines << " counted.\n";
      return 0;
    }
    

    Compiled and tested in cygwin64:

    $ g++ -std=c++11 -o line-count line-count.cc
    
    $ ./line-count line-count.cc - line-count.cc -
    File: 'line-count.cc', 32 counted.
    1
    2
    3
    File: '-', 3 counted.
    File: 'line-count.cc', 32 counted.
    1
    2
    3
    File: '-', 3 counted.
    Total: 70 counted.
    
    $
    

    So, it's indeed the trick with std::cin.clear() which resets the EOF flag in input stream and makes it possible to read again from the /dev/stdin.

    In the case of OP, the std::cin.ignore() after std::cin.clear() is IMHO wrong. It discards the first character of the re-enabled standard input which makes the following processing wrong (by not counting the first character).

    Davis (again) prodived a short but obvious explanation which I give in my words:

    With CtrlD, the standard input receives an EOF in the next attempt to read; and memorizes it in its internal flags. Nevertheless, the flag can be reset. If there isn't any further input the next read attempt would fail but otherwise, the input can just be continued.

    May be, it's worth to emphasize the internal EOF flag in std::ios. Without std::cin.clear(), the read attempt would fail even when more input is available. As long as the internal std::stream flags doesn't result in good state, no read attempt will be performed on lower level even though it might succeed.

    0 讨论(0)
提交回复
热议问题