C语言 Visual Studio代码如何在“r+”模式下处理文件I/O的fputs语句?

aor9mmx1  于 2023-01-25  发布在  其他
关注(0)|答案(3)|浏览(102)

我写了一个代码

#include <stdio.h>
#include <stdlib.h>
int main()
{
    FILE *fp;
    fp=fopen("lets.txt","r+");
    if(fp==NULL)
    {
        printf("ERROR");
        exit(1);
    }
    else
    {
        char ch,ch1;
        while(!feof(fp))
        {
           ch= fgetc(fp);
           printf("%c",ch);
        }
        printf("\n\nYou want to write something? (1/0)");
        int n;
        scanf("%d",&n);
        if(n==1)
        {
            fputs("Jenny",fp);
            ch1 = fgetc(fp);
            printf("%c\n", ch1);
            while(ch1 != EOF)
            {
                ch1=fgetc(fp);
                printf("%c",ch1);
            }
            fclose(fp);
        }
        else{
            printf("File Closed ");
            fclose(fp);
        }
    }
}

我尝试在已经存在的文件“lets.txt”

中插入字符串
但当我运行这段代码时,它显示在终端


我本来以为这只是把Jenny放进最终文件,但它也添加了其他文本,这些文本在它之前就存在,还有很多NULL。这是因为临时内存存储之类的原因,还是只是代码中的一些错误?

oxiaedzo

oxiaedzo1#

首先,这些线条

char ch,ch1;
while(!feof(fp))
{
    ch= fgetc(fp);
    printf("%c",ch);
}

都是错的。
如果你想保证ch能够表示值EOF,并且还想能够将它与每个可能的字符代码区分开,那么你必须将fgetc的返回值存储在int中,而不是char中。不是char。有关此问题的详细信息,请参阅this other answer
此外,函数feof将仅返回非零值(即true),如果上一个读取操作已经由于文件结束而失败。它不提供下一个读取操作是否会失败的任何指示。这意味着如果fgetc返回EOF,则将打印该值,就像fgetc成功一样,这是错误的。有关此问题的详细信息,请参见以下问题:
Why is “while( !feof(file) )” always wrong?
鉴于上述原因,我建议你将这些行改为:

int ch, ch1;

while ( ( ch = fgetc(fp) ) != EOF )
{
    printf( "%c", ch );
}

另一个问题是,当文件以更新模式打开时(即模式字符串中包含+,例如"r+"),你不能自由地在读和写之间切换。根据ISO C11标准§ 7.21.5.3 ¶ 7,

  • 输出后不应直接跟随输入,而不介入对fflush函数或文件定位函数(fseekfsetposrewind)的调用,以及
  • 除非输入操作遇到文件结束,否则在没有对文件定位函数的介入调用的情况下,输入之后不应直接跟随输出。

如果您违反了这些规则中的任何一条,那么您的程序将调用undefined behavior,这意味着任何事情都可能发生,包括您可能得到无效输出。
因此,我建议你把台词改一下

fputs("Jenny",fp);
ch1 = fgetc(fp);

致:

fseek( fp, 0, SEEK_CUR );
fputs("Jenny",fp);
fflush( fp );
ch1 = fgetc(fp);

fflush( fp );这一行相比,fseek( fp, 0, SEEK_CUR );这一行是绝对必要的,而根据上面提到的规则,实际上并不必要,因为你遇到了文件尾,但是无论如何保留这一行可能是一个好主意,例如,如果你后来改变你的程序,因为除了文件尾之外的其他原因而停止读取,在这种情况下,这一行是必需的。

zxlwwiss

zxlwwiss2#

Re:"我把while循环的条件改成了这个简单的形式-ch = fgetc(fp);while(ch!= EOF)但它仍然显示相同的结果。

    • getchar()返回的值必须存储在int:**中
ch= fgetc(fp);

ch已声明为char。将值存储在char中会使EOF测试不可靠。C17声明EOF具有负int值。在某些实现中,charunsigned,因此它不能表示负值。
在类型char有符号的实现中,假定EOF定义为-1(这是大多数实现的情况),不可能将EOF与字符代码255区分开来(字符代码255char中存储为值-1,而在int中存储为255)。
在手册页中:
fgec()、gec()和getchar()返回读取的字符,作为无符号字符转换为***int***或文件结束时的EOF或错误。
它还指出:
如果getchar()返回的整数值存储到char类型的变量中,然后与整数常量EOF进行比较,则比较可能永远不会成功,因为扩展为整数的char类型变量的符号扩展是由实现定义的。
其也与fgetc相关。

    • 可能的修复方法:**

ch声明为int

cgvd09ve

cgvd09ve3#

在读写之间切换时没有设置文件指针。MSVC man page表示大约fopen
但是,当从阅读切换到写时,输入操作必须遇到EOF标记。如果没有EOF,则必须使用对文件定位函数的干预调用。文件定位函数是fsetposfseekrewind。当从写切换到读时,你必须使用一个介入调用到fflush或到一个文件定位函数.

相关问题