Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
886 views
in Technique[技术] by (71.8m points)

c++ - Reuse std::cin after eating an EOF

The unix command wc has this functionality:

$ wc - - -
aaa bbb ccc<EOF>
0 3 11 -
aaa bbb ccc<EOF>
0 3 11 -
aaa bbb ccc<EOF>
0 3 11 -
0 9 33 total

Each <EOF> indicates a <C-d> key sequence that enters an EOF into stdin. wc is then able to pick up this EOF.

I'm trying to implement this in C++. A common suggestion is combination of clear() and ignore().

char c;
while (std::cin >> c) { ... }

std::cin.clear();
std::cin.ignore();

while (std::cin >> c) { /* never executed */ }

I've also tried std::cin.peekg(std::cin.beg), which also does not work.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 << "'!
";
    return 0;
  }
  for (std::string buffer; std::getline(in, buffer); ++nLines);
  std::cout << "File: '" << fileName << "', " << nLines << " counted.
";
  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.
";
  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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...