我在C ++中使用fscanf
如下:
这是我的文本文件:
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.I想知道为什么总是读取最后一个字符串2次。请咨询
答案 0 :(得分:2)
最后一个字符串未被读取两次。问题是循环测试继续:
(eof = fgetc(fp)) != eof
这会将fgetc()
的返回值分配给eof
并检查它是否等于eof
。在逻辑上很难做的事情。但是,当文件位于fgetc()
时调用EOF
时,它会返回-1
。对于赋值,它被转换为char
,但括号中的子表达式保留值-1
(由于类型提升规则)。比较-1
到255或-127(取决于char是有符号还是无符号)最终终止循环。
第三次循环,fscanf()
失败并且不更新str
:这就是为什么相同的值似乎已被读取两次。
要解决这个问题,最直接的技巧是:
do {
...
} while (!feof (fp));
但是,在许多操作系统上,feof()
与fscanf()
的效果不佳,因为在fscanf()
失败之前,文件结束指示无法可靠地设置。使用
do {
int result = fscanf (fp, ...whatever...);
if (result < 0) // end of file or i/o error?
break;
} while (!feof (fp));
答案 1 :(得分:1)
[跟随Christian Rau在另一个帖子中的评论,我已经 改变了我的第一点,以对应我现在意识到的事情。
您的代码存在一些问题。一些最明显的 是:
do...while
末尾的条件有未定义的行为。
在表达式eof = fgetc(fp)) != eof
中,您可以修改对象
(eof
),你可以在表达式的其他地方访问它
确定要存储的值。就标准而言,
任何事情都可能发生,事实上,不同的编译器会有所不同
的东西。
您要将fgetc
的结果分配给char
,而不是。{
到int
。 fgetc
的返回值在范围内
[0...UCHAR_MAX]
或EOF
(保证为负数)。在
换句话说,它可能比char
中的值多一个值。
然后将char
与EOF
进行比较的结果取决于是否
普通char
是否已签名。如果它没有签名,它永远不会有
负值,因此永远不会等于EOF
。如果签了,
然后在特定字符代码(拉丁语-1中的0xFF或'ÿ'
)上
检测到文件结束。 fgetc
的返回值应始终为。{1}}
已分配给int
,只能转换为char
在<{1}}。
您正在使用EOF
的结果而不检查。{
功能成功了。在C ++,IO中,无论是iostream还是fscanf
都不是
预测。由于界面的定义方式,它是
不可能事先告诉你是否会遇到档案结束。
您必须尝试阅读,然后测试读取是否成功。
您正在FILE*
使用fscanf
而不限制输入
长度。这是一个等待发生的缓冲区溢出。
在C ++中,编写你正在做的事情最自然的方式是:
char[]
使用较旧的C兼容流,您可以写:
std::string word;
while ( anIStream >> word ) {
// ...
}
在这两种情况下,检查成功都会控制循环;如果是 在C接口中,您使用格式宽度说明符来确保您 不要超出缓冲区。 (在这两种情况下,你必须定义 即使您只使用,也会在循环外读取变量 他们在循环中。)
答案 2 :(得分:0)
while( (eof = fgetc(fp)) != EOF)