当 (( c = getc(file)) != EOF) 循环不会停止执行。

5

我无法弄清楚为什么我的while循环不起作用。如果没有它,代码运行得很好...代码的目的是在二进制文件中查找秘密消息。所以我让代码找到了这些字母,但是当我试图让它循环到文件结尾时,它就无法工作。我对此很陌生。我做错了什么吗?

main(){

FILE* message;
int i, start;
long int size;
char keep[1];

message = fopen("c:\\myFiles\\Message.dat", "rb");

if(message == NULL){
    printf("There was a problem reading the file. \n");
    exit(-1);
}

//the first 4 bytes contain an int that tells how many subsequent bytes you can throw away
fread(&start, sizeof(int), 1, message);
printf("%i \n", start); //#of first 4 bytes was 280
fseek(message, start, SEEK_CUR); //skip 280 bytes
keep[0] = fgetc(message); //get next character, keep it
printf("%c", keep[0]); //print character

while( (keep[0] = getc(message)) != EOF) {
    fread(&start, sizeof(int), 1, message);
    fseek(message, start, SEEK_CUR);
    keep[0] = fgetc(message);
    printf("%c", keep[0]);
}

fclose(message);

system("pause");
}

编辑:

在调试器中查看我的代码后,发现while循环中有"getc"使一切混乱了。我通过创建一个名为letter的新字符并将代码替换为以下内容来修复它:

fread(&start, sizeof(int), 1, message);
fseek(message, start, SEEK_CUR);

while( (letter = getc(message)) != EOF) {
    printf("%c", letter);
    fread(&start, sizeof(int), 1, message);
    fseek(message, start, SEEK_CUR);
}

现在它运行得非常好。欢迎提出更多建议。谢谢大家。

你尝试过用feof替换循环条件吗? - RonaldBarzell
4
这会导致错误,请记住 feof() 只有在尝试读取超过最后一个字节之后才会返回 true。 - dreamlax
@user1161318:feof基本上永远不正确。 - Kerrek SB
2
没有必要拥有只有一个元素的数组 - 只需使用 char keep 并且在所有地方都去掉 [0] - GraphicsMuncher
1
实际上,应该将keep设置为int类型以进行修复。请参考JonathanLeffler的答案。对于@user1695758,如果Jonathan的答案解决了问题,请务必接受它。 - RonaldBarzell
显示剩余2条评论
2个回答

19
getc()及其相关函数的返回值是int而不是char
如果将getc()的结果赋给char类型的变量,当其返回EOF时,会出现以下两种情况之一:
  • 如果普通的char类型是无符号类型,则EOF将转换为0xFF,并且0xFF!= EOF,因此循环永远不会终止。
  • 如果普通的char类型是有符号类型,则EOF等效于一个有效字符(在8859-1代码集中,它是ÿ,y-umlaut,U+00FF,LATIN SMALL LETTER Y WITH DIAERESIS),因此您的循环可能会提前终止。
鉴于您面临的问题,我们可以暂时猜测您将普通的char类型作为无符号类型。 getc()等函数返回int的原因是,它们必须返回能够适应char类型的每个可能值以及一个不同的值EOF。在C标准中,它说道:

ISO/IEC 9899:2011 §7.21.7.1函数fgetc()

int fgetc(FILE *stream);

如果指向stream所指向的输入流的文件结束标志未设置,并且下一个字符存在,则fgetc函数将该字符作为转换为intunsigned char获取 ...

如果流的文件结束标志已设置或者流位于文件的结尾,则为该流设置文件结束标志,并且fgetc函数返回EOF。

getc()函数和getchar()函数具有类似的措辞:它们被定义为像fgetc()函数一样运行,只是如果getc()被实现为宏,则它可以对不通常授予标准宏的文件流参数进行处理 - 具体而言,流参数表达式可能会计算多次,因此使用带副作用的getc(fp++)非常愚蠢(但更改为fgetc()将是安全的,但仍然是古怪的)。
在您的循环中,可以使用:
int c;

while ((c = getc(message)) != EOF) {
    keep[0] = c;

这样做保留了对keep[0]的赋值;不确定你是否真正需要它。

你应该检查对fgets()getc()fread()的其他调用,以确保你得到了预期的输入。尤其是在输入方面,你不能真正承担跳过这些检查的风险。比较快,而不是慢,一些东西会出问题,如果你没有认真地检查返回状态,你的代码很可能会崩溃或者只是出现错误。


+1 我也认为这是问题所在,但不知道如何简洁地表达出来。 - dreamlax
也给你点赞了。建议你明确告诉他如何修复(将keep声明为int),这样就没有理由不接受你的答案了。 - RonaldBarzell
谢谢!现在我对发生的事情更加理解了。 - user1695758

2

getc() 可能返回 256 种不同的 char 值,并存储在像 keep [0] 这样的 char 变量中(是的,我正在过度概括)。为了可靠地检测文件结尾,EOF 必须具有与它们所有值都不同的值。这就是为什么 getc() 返回 int 而不是 char 的原因:因为第 257 个独特的值对于 EOF 不适合于 char

因此,您需要将 getc() 返回的值至少存储在一个 int 中,直到您将其与 EOF 进行比较为止:

int tmpc;
while( (tmpc = getc(message)) != EOF) {
    keep[0] = tmpc;
    ...

或许应该这么说:"因为 257 个不同的值不能放入 char 中。" OP 的系统,肯定是 Windows,有符号 char 可以容纳 EOF,它通常是 -1。只是无法区分 EOF(char) 255 - chux - Reinstate Monica
1
@chux:确实,这就是“distinct”的意思。 - John Marshall

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