食用EOF后重用std :: cin

时间:2018-09-14 02:22:10

标签: c++ io stdin cin istream

unix命令wc具有以下功能:

$ 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

每个<EOF>指示一个<C-d>键序列,该键序列将一个EOF输入到stdin中。然后wc就能拿起这个EOF

我正在尝试用C ++实现。常见的建议是将clear()ignore()组合在一起。

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

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

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

我也尝试过std::cin.peekg(std::cin.beg),但这也行不通。

1 个答案:

答案 0 :(得分:1)

在戴维斯的患者帮助下,我了解了用 Ctrl + D 完成的键入输入与我不知道的here文档之间的区别。 (我向戴维斯·鲱鱼致敬。)

一次,我明白了C ++中的其余部分非常简单,就像我的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;
}

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.

$

因此,std::cin.clear()的确是诀窍,它可以重置输入流中的EOF标志,并可以再次从/dev/stdin进行读取。

对于OP,std::cin.clear()之后的std::cin.ignore()是恕我直言。它将丢弃重新启用的标准输入的第一个字符,从而使随后的处理错误(不计算第一个字符)。

戴维斯(再次)做了一个简短但显而易见的解释,我用我的话说:

使用 Ctrl D ,标准输入将在下次尝试读取时收到EOF;并在其内部标志中存储它。但是,该标志可以重置。如果没有其他输入,则下一次读取尝试将失败,但否则,可以继续输入。

也许值得强调一下std::ios中的内部EOF标志。如果没有std::cin.clear(),则即使有更多输入可用,读取尝试也会失败。只要内部std::stream标志不会处于良好状态,即使可能成功,也不会在较低级别上执行读取尝试。