1

fscanf在 C++ 中使用如下:

这是我的文本文件:

abcd
efgh

“efgh”后没有空格或换行。现在这是我的循环:

FILE* fp;
char eof = 0;
do
{
    char str[20];
    fscanf(fp, "%s", str);
    std::cout<<str<<std::endl;
}
while((eof = fgetc(fp)) != eof)

我期望的输出是:

abcd
efgh

但我得到的实际输出是:

abcd
efgh
efgh

我调试了一个发现,在读取“efgh”之后,读入 eof 的值是 '\n' 而不是 EOF。环境是linux mint。我想知道为什么最后一个字符串总是被读取2次。请指教

4

3 回答 3

2

最后一个字符串没有被读取两次。问题是循环的继续测试:

(eof = fgetc(fp)) != eof

这将fgetc()' 的返回值分配给eof并检查它是否不等于eof。逻辑上很难做到的事情。但是,当fgetc()文件位于 时调用时EOF,它会返回-1。这被强制转换为 a char,但括号中的子表达式保留该值-1(由于类型提升规则)。与-1255 或 -127 相比(取决于 char 是有符号还是无符号)最终终止循环。

第三次通过循环,fscanf()失败并且不更新str:这就是为什么相同的值似乎被读取了两次。

要修复它,最直接的技术是:

do {
 ...
} while (!feof (fp));

但是,在许多操作系统上,feof()它不能很好地工作,fscanf()因为文件结束指示直到fscanf()失败才可靠设置。一种更可靠、抗 O/S 的技术是使用

do {
    int result = fscanf (fp, ...whatever...);
    if (result < 0)   // end of file or i/o error?
         break;
} while (!feof (fp));
于 2012-10-11T05:36:22.823 回答
1

[在另一个线程中跟进 Christian Rau 的评论,我已经将我的第一点更改为与我现在意识到的相对应]

您的代码有几个问题。一些最明显的是:

  • 您末尾的条件do...while具有未定义的行为。在表达式eof = fgetc(fp)) != eof中,您修改一个对象 ( eof),并在表达式中的其他地方访问它,而不是确定要存储的值。就标准而言,任何事情都可能发生,实际上不同的编译器会做不同的事情。

  • 您将结果分配fgetc给 a char,而不是 a int。的返回值fgetc要么在范围内 [0...UCHAR_MAX],要么是EOF(保证为负)。换句话说,它可以比 a 中的值多一个char。然后比较charwith的结果EOF取决于 plainchar是否签名。如果它没有签名,它永远不会有负值,因此永远不会等于EOF。如果已签名,则特定字符代码(0xFF 或'ÿ'latin-1)将被检测为文件结尾。的返回值fgetc应始终分配给 a int,并且char仅应 在测试后转换为EOF.

  • 您使用的结果fscanf没有检查函数是否成功。在 C++ 中,IO,无论是 iostream 还是FILE*不可预测 的。由于接口的定义方式,无法提前判断是否会遇到文件结尾。您必须尝试读取,然后测试读取是否成功。

  • 您正在使用fscanfinto achar[]而不限制输入长度。这是等待发生的缓冲区溢出。

在 C++ 中,写你正在做的事情的最自然的方式是:

std::string word;
while ( anIStream >> word ) {
    //  ...
}

使用较旧的 C 兼容流,您将编写:

char word[20];
while ( fscanf( fp, "%19s", word ) == 1 ) {
    //  ...
}

在这两种情况下,成功检查都会控制循环;在 C 接口的情况下,您使用格式宽度说明符来确保您不会超出缓冲区。(在这两种情况下,您都必须定义在循环外读取的变量,即使您只会在循环中使用它们。)

于 2012-10-11T07:36:10.147 回答
0

你正在用 eof 检查 eof。尝试像这样检查

while( (eof = fgetc(fp)) != EOF)
于 2012-10-11T05:36:19.763 回答