简单的C++程序无法读取EOF。

4
我很难理解为什么while (cin.get(Ch))看不到EOF。我从一个有3个单词的文本文件中读取,当我调试我的WordCount时是3(正是我希望的)。然后它回到了while循环并卡住了。此时Ch没有值。我认为在换行符后它会读取EOF并跳出循环。我不能使用< fstream>,必须在DOS中使用重定向。非常感谢。
#include <iostream>
using namespace std;

int main()
{
    char Ch = ' ';
    int WordCount = 0;
    int LetterCount = 0;

    cout << "(Reading file...)" << endl;

    while (cin.get(Ch))
    {
        if ((Ch == '\n') || (Ch == ' '))
        {
            ++WordCount;
            LetterCount = 0;
        }
        else
            ++LetterCount;
    }

    cout << "Number of words => " << WordCount << endl;

    return 0;
}

当我在VS中运行它时,它会卡住。当我在DOS终端中重定向一个三个单词的文件(“这很有趣!”)时,它只显示两个单词。 - c3ad-io
你真的应该计算单词数量,还是只计算空格和换行符?例如,a.b..c...d(其中.代表一个空格)?它有四个单词,但是六个空格? - Aaron McDaid
while循环在Mac和Linux上使用clang和gcc工作正常(即程序读取EOF并退出循环)。它不产生正确的输出,但这是可以预期的,因为单词计数有误。 cparnin,你正在使用什么编译器? - David Hammen
感谢你的帮助,Aaron McDaid。这只是项目的一部分。我需要读入文本,然后输出不同单词长度的频率-然后输出平均单词长度。单词仅计算为字母字符、数字和撇号。这是201课程的内容,我再次感到在某些简单的事情上卡住了。像这样的事情让人怀疑未来。永不放弃。也许当我在“某些简单的事情”上卡住时,我需要学会放手。感谢你的时间! - c3ad-io
5个回答

2
while (cin >> Ch)
{   // we get in here if, and only if, the >> was successful
    if ((Ch == '\n') || (Ch == ' '))
    {
        ++WordCount;
        LetterCount = 0;
    }
    else
        ++LetterCount;
}

这是安全且常见的方式,以最小的变更来安全地重写您的代码。

(您的代码很不寻常,试图扫描所有字符并计算空格和换行符。我将对一个稍微不同的问题给出更一般的答案-如何读入所有单词。)

检查流是否结束的最安全方法是if(stream)。要注意if(stream.good()) -它并不总是按预期工作,并且有时会过早退出。最后一次对char>>操作不会将我们带到EOF,但是最后一次对intstring>>操作会将我们带到EOF。这种不一致可能会令人困惑。因此,使用good()或任何其他测试测试EOF是不正确的。

string word;
while(cin >> word) {
   ++word_count;
}

if(cin)if(cin.good())之间存在重要的区别。前者是operator bool conversion。通常,在这种情况下,您想测试:
“上一次提取操作成功还是失败?”
这与以下内容不同:
“我们现在是否已到达EOF?”
cin >> word读取最后一个单词后,字符串处于EOF。但是,word仍然有效并包含最后一个单词。
简而言之:eof位不重要,bad位很重要。这告诉我们上次提取失败了。

1

计数

该程序将换行符和空格字符视为单词。在你的文件内容 "this if fun!" 中,我看到两个空格和没有换行符。这与观察到的输出一致,表明有两个单词。

你是否尝试使用十六进制编辑器或类似工具查看文件的确切内容?

你还可以更改程序,在循环中读取的最后一个字符是字母时,再计算一个单词。这样,你就不必拥有以换行符结尾的输入文件。

循环终止

我无法解释你的循环终止问题。while-条件看起来很好。istream::get(char&)返回流引用。在while条件中,根据你的编译器实现C++级别的不同,operator booloperator void* 将应用于引用,以指示是否可能进行进一步读取。

习惯用法

从流中读取的标准习惯用法是

char c = 0;
while( cin >> c )
   process(c);

我没有严重的理由不会偏离它。


回到 while 循环并卡住了。请处理。 - Mad Physicist
@疯狂物理学家:已解决。 - Peter G.

0
在您的情况下,退出循环的正确方法是:
while (cin.good()) {
  char Ch = cin.get();
  if (cin.good()) {
    // do something with Ch
  }
}

话虽如此,你尝试做的事情可能有更好的方法。


有很多正确的方法来终止输入循环。你和cparnin的方法在我看来都是正确的。 - Peter G.
good()是危险的。对于char来说,它足够安全,但通常会提前退出一次迭代。例如,如果您有int i;cin >> i,那么没有安全的方法可以使用good - Aaron McDaid
@AaronMcDaid 如果你注意到了,在我的代码片段中没有 int i; cin >> i;,事实上,我有意地使用了 get()。你的评论 "good() 很危险" 过于泛化,就像说 "C++ 是危险的" 一样。如果用得不当,两者都是危险的。 - CAFxX

0

你输入的文件是

这很有趣!{EOF}

两个空格会使WordCount增加到2,然后EOF,退出循环!如果你添加一个新行,你输入的文件就是

this is fun!\n{EOF}

1
@Chad Parnin 我认为你应该检查最后一个字符,如果是空格或换行符,则保留 wordCount,如果不是,则增加 wordCount。 - 0xFFFFFFFF

0

我拿了你的程序在Visual Studio 2013中加载,将cin更改为一个fstream对象,该对象打开了一个名为stuff.txt的文件,其中包含确切的字符"This is fun!/n/r",程序运行正常。正如之前的答案所指出的那样,要小心,因为如果文本末尾没有/n,程序将会漏掉最后一个单词。然而,我无法复制应用程序陷入无限循环的情况。按照编写的代码,我认为它是正确的。

cin.get(char)返回对istream对象的引用,然后调用它的operator bool(),当任何错误位被设置时返回false。有一些更好的方法来编写这段代码以处理其他错误条件...但是这段代码对我有效。


谢谢。这是我需要知道的事情。我以为文本文件中的文本自动在末尾有EOF。这就是为什么我会少一个单词。至于Visual Studio:当我运行它时,它等待输入,然后我输入一些内容,但它却一直挂起…… - c3ad-io
需要注意的是,实际上没有EOF字符(至少在大多数操作系统上)。EOF是一种条件,当达到输入流的末尾时会发生。如果您正在使用cin,则程序将无限制地继续请求输入。您可以输入内容,程序会处理它们,然后继续请求更多内容。当应用程序挂起时,您能否继续输入? - Darinth
是的,就像你说的那样它一直在询问。我想我从来没有看到过它如何挂起,但当我给它一个文件时,它会给我一个words-1。我认为解决这个问题的简单方法是设置一个不同的条件并且cout << WordCount +1。 - c3ad-io

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接