从C中读取和打印文件时的奇数输出

时间:2014-06-17 19:48:23

标签: c io stream

我写了一个程序,它将读取并输出自己的源文件的内容。我的目的主要是学习如何使用I / O流和" FILE"类型。我在Linux Ubuntu 14.04上的纯文本文档中编写了程序,并使用终端编译和运行程序。这是从编译到完成的终端内容:

joseph@ubuntu:~/Desktop$ gcc test.c
joseph@ubuntu:~/Desktop$ ./a.out

File Opened

#include<stdio.h>
#define fileLocation ("/home/joseph/Desktop/test.c")
#define MAXREAD 1000

int main(void)
{
    char fileContents[MAXREAD];
    int i;

    FILE *tf;
    tf = fopen(fileLocation, "r");

    printf("File Opened\n");
    for(i=0;fileContents[i] != EOF; i++)
    {
        fileContents[i] = fgetc(tf);
        printf("%c", fileContents[i]);
    }
    fclose(tf);
    printf("\nFile Closed\n");
    return 0;
}
************************************************************

File Closed

*符号实际上是Unicode(0 + FFFD:替换字符),但我似乎无法输入。

我的问题是,为什么它不会在最后的结束大括号结束程序而是打印一堆替换字符?

1 个答案:

答案 0 :(得分:3)

循环中的顺序不正确。您应该在存储和打印字符值之前检查EOF 。您还应该确保不会超越数组边界。

int main(void)
{
    char fileContents[MAXREAD];
    int i, c;

    FILE *tf = fopen(fileLocation, "r");
    if (tf == NULL)
    {
        perror(fileLocation);
        return EXIT_FAILURE;
    }

    printf("File Opened\n");
    for (i=0; i < MAXREAD && (c = fgetc(tf)) != EOF; ++i)
    {
        fileContents[i] = c;
        fputc(fileContents[i], stdout);
    }
    fclose(tf);
    printf("\nFile Closed\n");
    return 0;
}

您的代码版本包括在char中打印错误存储的EOF(这本身就是另一个问题,但首先不存储它可以避免)。但这远远不是你困境的结束。用于继续for循环的条件逻辑是错误的。实际上,由于您从未初始化fileContents[],因此它实际上会调用未定义的行为。每次迭代,您都要检查一个尚未编写或未初始化的阵列插槽。继续阅读如何/为什么

为什么要继续打印

在每次循环迭代之前计算控制表达式fileContents[i] != EOF。增量表达式i++每次迭代后执行,但下一次评估控件条件之前执行。从标准:

  

声明

     

for ( clause-1 ; expression-2 ; expression-3 ) statement

     

表现如下:表达式expression-2是在每次执行循环体之前计算的控制表达式。在每次执行循环体后,表达式expression-3被计算为void表达式。如果clause-1是一个声明,它声明的任何标识符的范围是声明的剩余部分和整个循环,包括其他两个表达式;它是在控制表达式的第一次评估之前按执行顺序到达的。如果clause-1是表达式,则在第一次评估控制表达式之前将其计算为void表达式。

直言不讳地说,您刚刚保存在fileContents[i]中的EOF永远不会被检查,因为i在下次评估之前会增加。根据以上描述,这是有意义的。这就是简单循环的原因:

for (i=0; i<N; ++i)
    dostuff;

退出时i < N false 。除非在dostuff中进行无法预料的修改,否则循环将以i = N终止。

同样,eval在增量步骤之后完成,在你的情况下如此:

for(i=0; fileContents[i] != EOF; i++)

在每次进入循环体之前评估控制表达式fileContents[i] != EOF。增量表达式在循环体后发生,但下一次评估control-expression之前发生。在循环体内,您将EOF存储在使用当前值i索引的广告位中。然后身体结束,i递增,然后你才检查一个你没有写过任何东西的插槽(还)。这一直持续到某一点,如果您(非)幸运,您会在新更新的EOF索引中发现i等效值。因此你终止了(但很可能,你很久以前就崩溃了。)