C语言从入门到实战——文件操作

文件操作

  • 前言
  • 一、 为什么使用文件
  • 二、 什么是文件
    • 2.1 程序文件
    • 2.2 数据文件
    • 2.3 文件名
  • 三、 二进制文件和文本文件
  • 四、 文件的打开和关闭
    • 4.1 流和标准流
      • 4.1.1 流
      • 4.1.2 标准流
    • 4.2 文件指针
    • 4.3 文件的打开和关闭
    • 4.4 文件的路径
  • 五、 文件的顺序读写
    • 5.1 顺序读写函数介绍
      • fgetc
      • fputc
      • fgets
      • fputs
      • fscanf
      • fprintf
      • fread
      • fwrite
    • 5.2 对比一组函数
      • sscanf
      • sprintf
    • 5.3标准的数据拷贝
  • 六、 文件的随机读写
    • 6.1 fseek
    • 6.2 ftell
    • 6.3 rewind
  • 七、 文件读取结束的判定
    • fgetc
    • fgets
    • 7.1 被错误使用的 feof
  • 八、 文件缓冲区


前言

C语言中的文件操作是通过使用文件指针来实现的。可以使用标准库中的函数来打开、读取、写入和关闭文件。

下面是一些常见的文件操作函数:

  1. 打开文件:使用 fopen() 函数来打开文件,该函数接受两个参数,文件名和打开方式。打开方式可以是 "r"(只读模式),"w"(写入模式),"a"(追加模式)等等。函数返回一个文件指针,可以在后续的操作中使用。
FILE *fp;
fp = fopen("filename.txt", "r");
  1. 读取文件内容:使用 fscanf() 函数来从文件中读取内容。该函数接受一个文件指针和一个格式字符串作为参数,读取文件中的数据并按照格式字符串的指示将数据存储到相应的变量中。
int num;
fscanf(fp, "%d", &num);
  1. 写入文件内容:使用 fprintf() 函数来向文件中写入内容。该函数接受一个文件指针和一个格式字符串作为参数,将相应的数据按照格式字符串的指示写入文件。
int num = 10;
fprintf(fp, "%d", num);
  1. 关闭文件:使用 fclose() 函数来关闭文件,该函数接受一个文件指针作为参数。
fclose(fp);

在进行文件操作时,需要注意以下几点:

  • 在打开文件之前要确保文件存在,或者在打开文件时使用合适的打开方式。
  • 在读取和写入文件时要确保文件指针指向正确的位置,可以使用 fseek() 函数来调整文件指针的位置。
  • 每次读取或写入文件后,都要检查函数的返回值,以确保文件操作成功。
  • 在完成文件操作后,一定要关闭文件,释放文件指针所占用的资源。

这是一个简单的文件操作的示例:

#include <stdio.h>

int main() {
    FILE *fp;
    int num;

    // 打开文件
    fp = fopen("numbers.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件\n");
        return 1;
    }

    // 读取文件内容
    fscanf(fp, "%d", &num);
    printf("文件中的数字是:%d\n", num);

    // 关闭文件
    fclose(fp);

    return 0;
}

在上面的示例中,程序打开了一个名为 numbers.txt 的文件,读取文件中的一个整数,并打印出来。最后关闭了文件。


一、 为什么使用文件

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。

二、 什么是文件

在这里插入图片描述
磁盘上的文件是文件。

但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。

文件的本质是结构体

2.1 程序文件

程序文件包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)。

2.2 数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。

在以前文章中所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。

其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

2.3 文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

文件名包含3部分:文件路径+文件名主干+文件后缀

例如: c:\code\test.txt

为了方便起见,文件标识常被称为文件名。

三、 二进制文件和文本文件

根据数据的组织形式,数据文件被称为文本文件或者二进制文件。

数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件。

如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件。

一个数据在内存中是怎么存储的呢?

字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2019测试)。
在这里插入图片描述

#include <stdio.h>
int main()
{
	int a = 10000;
	FILE* pf = fopen("test.txt", "wb");
	fwrite(&a, 4, 1, pf); //二进制的形式写到文件中
	fclose(pf);
	pf = NULL;
	return 0;
}

在VS上打开二进制文件:
在这里插入图片描述
在这里插入图片描述

四、 文件的打开和关闭

4.1 流和标准流

4.1.1 流

我们程序的数据需要输出到各种外部设备,也需要从外部设备获取数据,不同的外部设备的输入输出操作各不相同,为了方便程序员对各种设备进行方便的操作,我们抽象出了流的概念,我们可以把流想象成流淌着字符的河。

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。

一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

4.1.2 标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?

标准输入输出流是默认打开的,可以直接使用

那是因为C语言程序在启动的时候,默认打开了3个流:

  • stdin-标准输入流,在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。
  • stdout-标准输出流,大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出流中。
  • stderr-标准错误流,大多数环境中输出到显示器界面。

这是默认打开了这三个流,我们使用scanfprintf等函数就可以直接进行输入输出操作的。

stdinstdoutstderr三个流的类型是: FILE* ,通常称为文件指针。

C语言中,就是通过 FILE* 的文件指针来维护流的各种操作的。

4.2 文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。

每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名FILE.

例如,VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型申明:

struct _iobuf {
	char *_ptr;
	int _cnt;
	char *_base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char *_tmpfname;
};
typedef struct _iobuf FILE;

不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异。

每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。

一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

下面我们可以创建一个FILE*的指针变量:

FILE* pf; //文件指针变量

定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。

比如:

在这里插入图片描述
创建文件变量,data的数据会拷贝到f 这个文件结构体中,将这个文件结构体填满
在这里插入图片描述

4.3 文件的打开和关闭

文件操作,操作成功返回文件指针,操作失败会返回NULL

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。

在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。

ANSIC 规定使用 fopen 函数来打开文件,fclose 来关闭文件。

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );

mode表示文件的打开模式,下面都是文件的打开模式:

文件使用方式含义如果指定文件不存在
r”(只读)为了输入数据,打开一个已经存在的文本文件出错
w”(只写)为了输出数据,打开一个文本文件建立一个新的文件
a”(追加)向文本文件尾添加数据建立一个新的文件
rb”(只读)为了输入数据,打开一个二进制文件出错
wb”(只写)为了输出数据,打开一个二进制文件建立一个新的文件
ab”(追加)向一个二进制文件尾添加数据建立一个新的文件
r+”(读写)为了读和写,打开一个文本文件出错
w+”(读写)为了读和写,建议一个新的文件建立一个新的文件
a+”(读写)打开一个文件,在文件尾进行读写建立一个新的文件
rb+”(读写)为了读和写打开一个二进制文件出错
wb+”(读写)为了读和写,新建一个新的二进制文件建立一个新的文件
ab+”(读写)打开一个二进制文件,在文件尾进行读和写建立一个新的文件

ps:
除了有关追加的,其余的像w和r的,在使用存入的时候会清空里面的数据,而不会保留,想要保留,要使用追加

/* fopen fclose example */
#include <stdio.h>
int main ()
{
	FILE * pFile;
	//打开文件
	pFile = fopen ("myfile.txt","w");
	//文件操作
	if (pFile!=NULL)
	{
		fputs ("fopen example",pFile);
		//关闭文件
		fclose (pFile);
	}
	return 0;
}

在这里插入图片描述

4.4 文件的路径

C语言文件的路径指向文件在计算机中的位置。路径可以是绝对路径,也可以是相对路径。

绝对路径是从计算机文件系统的根目录开始的完整路径。例如,Windows系统中的绝对路径可能是"C:\Users\Administrator\Desktop\example.c",而Unix/Linux系统中的绝对路径可能是"/home/user/example.c"

相对路径是相对于当前工作目录的路径。当前工作目录是执行程序或脚本的位置。例如,当前工作目录是"/home/user",文件位于当前工作目录下的子目录"code"中,则相对路径可能是"code/example.c"

在C语言中,使用文件操作函数打开、读取和写入文件时,需要提供文件的路径作为参数。使用绝对路径可以确保准确找到文件,而使用相对路径可以简化文件路径的书写。

在VS中,路径需要双斜杠// .表示当前路径 ..表示上一级路径

绝对路径
在这里插入图片描述
相对路径

当前路径的上一级路径的上一级路径
在这里插入图片描述

五、 文件的顺序读写

5.1 顺序读写函数介绍

函数名功能适用于
fgetc字符输入函数所有输入流
fputc字符输出函数所有输出流
fgets文本行输入函数所有输入流
fputs文本行输出函数所有输出流
fscanf格式化输入函数所有输入流
fprintf格式化输出函数所有输出流
fread二进制输入文件
fwrite二进制输出文件

所有流 分为标准输入输出流和文件输入输出流

上面说的适用于所有输入流一般指适用于标准输入流和其他输入流(如文件输入流);

所有输出流一般指适用于标准输出流和其他输出流(如文件输出流)。

fgetc

见下面

fputc

c语言的fputc函数用于将一个字符写入到文件中。该函数的原型如下:

int fputc(int c, FILE *stream);

其中,c表示要写入的字符,stream表示要写入的文件指针。该函数返回写入的字符,如果出现错误,则返回EOF

下面是一个示例:

#include <stdio.h>

int main() {
    FILE *fp;
    char c = 'A';

    // 打开文件
    fp = fopen("file.txt", "w");

    // 写入字符
    fputc(c, fp);

    // 关闭文件
    fclose(fp);

    return 0;
}

上述示例将字符'A'写入到名为file.txt的文件中。

fgets

见下面

fputs

C语言的fputs函数用于将字符串写入到文件中。该函数的原型如下:

int fputs(const char *str, FILE *stream);

其中,str表示要写入的字符串,stream表示要写入的文件指针。该函数返回非负值表示成功,返回EOF表示出现错误。

下面是一个示例:

#include <stdio.h>

int main() {
    FILE *fp;
    char str[] = "Hello, World!";

    // 打开文件
    fp = fopen("file.txt", "w");

    // 写入字符串
    fputs(str, fp);

    // 关闭文件
    fclose(fp);

    return 0;
}

上述示例将字符串"Hello, World!"写入到名为file.txt的文件中。

fscanf

C语言中的 fscanf 函数用于从文件中读取数据。它的格式与 scanf 函数类似,但需要指定要读取的文件。

int fscanf(FILE *stream, const char *format, ...);

其中,stream 是指向要读取的文件的指针,format 是要读取的数据的格式字符串,... 表示可以接收任意数量和类型的参数,用来接收读取的数据。

下面是一个示例:

#include <stdio.h>

int main() {
    FILE *file;
    int num1, num2;
    
    file = fopen("data.txt", "r"); // 打开文件
    
    if(file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    
    fscanf(file, "%d %d", &num1, &num2); // 从文件中读取两个整数
    
    printf("从文件中读取的两个整数分别为:%d 和 %d\n", num1, num2);
    
    fclose(file); // 关闭文件
    
    return 0;
}

上述示例中,我们首先使用 fopen 函数打开名为 "data.txt" 的文件。然后,使用 fscanf 函数从文件中读取两个整数,并将它们存储到变量 num1num2 中。最后,使用 printf 函数将读取的两个整数输出到屏幕上。

需要注意的是,在使用 fscanf 函数读取文件数据之前,我们需要确保文件已经成功打开,并且在使用完毕后需要使用 fclose 函数关闭文件。

fprintf

C语言中的 fprintf 函数用于将指定的数据按照指定的格式写入到文件中。它的格式与 printf 函数类似,但需要指定要写入的文件。

int fprintf(FILE *stream, const char *format, ...);

其中,stream 是指向要写入的文件的指针,format 是要写入的数据的格式字符串,... 表示可以接收任意数量和类型的参数,用来传递要写入的数据。

下面是一个示例:

#include <stdio.h>

int main() {
    FILE *file;
    int num = 10;
    
    file = fopen("data.txt", "w"); // 打开文件
    
    if(file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    
    fprintf(file, "The number is %d\n", num); // 将整数写入文件
    
    fclose(file); // 关闭文件
    
    return 0;
}

上述示例中,我们首先使用 fopen 函数打开名为 "data.txt" 的文件,并指定以写入方式打开文件。然后,使用 fprintf 函数将整数 num 写入文件,同时按照指定的格式进行格式化输出。最后,使用 fclose 函数关闭文件。

需要注意的是,在使用 fprintf 函数写入文件数据之前,我们需要确保文件已经成功打开,并且在使用完毕后需要使用 fclose 函数关闭文件。

fread

在 C 语言中,fread 函数用于从文件中读取指定数量的数据,并将其存储到缓冲区中。它的函数原型如下:

size_t fread(void *ptr, size_t size, size_t count, FILE *stream);

其中,ptr 是一个指向要读取数据的缓冲区的指针,size 是每个数据项的大小(以字节为单位),count 是要读取的数据项的数量, stream 是一个指向要读取的文件的指针。

fread 函数会返回实际读取的数据项数量,若返回值小于 count,可能是因为已经到达了文件结尾或发生了读取错误。

下面是一个示例,演示如何使用 fread 从文件中读取数据:

#include <stdio.h>

int main() {
    FILE *file;
    int numbers[5];
    
    file = fopen("data.txt", "r"); // 打开文件
    
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    
    fread(numbers, sizeof(int), 5, file); // 从文件中读取5个整数
    
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    
    fclose(file); // 关闭文件
    
    return 0;
}

在上述示例中,我们首先使用 fopen 函数打开名为 "data.txt" 的文件,并指定以只读方式打开文件。然后,使用 fread 函数从文件中读取 5 个整数,将其存储在 numbers 数组中。最后,使用循环输出读取到的整数。

需要注意的是,在使用 fread 函数读取文件数据之前,我们需要确保文件已经成功打开,并且在使用完毕后需要使用 fclose 函数关闭文件。

fwrite

在 C 语言中,fwrite 函数用于将数据从内存写入到文件中。它的函数原型如下:

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

其中,ptr 是指向要写入的数据的指针,size 是每个数据项的大小(以字节为单位),count 是要写入的数据项的数量,stream 是一个指向要写入的文件的指针。

fwrite 函数会返回实际写入的数据项数量,若返回值小于 count,可能是因为写入错误或者磁盘已满。

下面是一个示例,演示如何使用 fwrite 将数据写入文件:

#include <stdio.h>

int main() {
    FILE *file;
    int numbers[] = {1, 2, 3, 4, 5};
    
    file = fopen("data.txt", "w"); // 打开文件
    
    if (file == NULL) {
        printf("无法打开文件\n");
        return 1;
    }
    
    fwrite(numbers, sizeof(int), 5, file); // 将5个整数写入文件
    
    fclose(file); // 关闭文件
    
    return 0;
}

在上述示例中,我们首先使用 fopen 函数以写入方式打开名为 “data.txt” 的文件。然后,使用 fwrite 函数将 numbers 数组中的 5 个整数写入文件。最后,使用 fclose 函数关闭文件。

需要注意的是,在使用 fwrite 函数写入文件数据之前,我们需要确保文件已经成功打开,并在使用完毕后使用 fclose 函数关闭文件。

5.2 对比一组函数

scanf/fscanf/sscanf
printf/fprintf/sprintf

fscanffprintf ,跟printfscanf 相比 只是前面多了一个文件指针

sscanf

sscanf函数是C语言中的一个输入函数,用于从字符串中按照指定的格式解析数据。它的函数原型为:

int sscanf(const char* str, const char* format, ...);

其中,str是要解析的字符串,format是解析的格式字符串,…表示可变参数列表,表示要解析的数据的地址。

sscanf函数根据format中的格式指定符,从str中读取数据,并将解析出的数据存放在可变参数列表中的相应位置。它可以解析各种类型的数据,比如整数、浮点数、字符等。

下面是一个使用sscanf函数的例子:

#include <stdio.h>

int main() {
    char str[] = "10 3.14 Hello";
    int a;
    float b;
    char c[10];
    sscanf(str, "%d %f %s", &a, &b, c);
    printf("a = %d, b = %f, c = %s\n", a, b, c);
    return 0;
}

输出结果为:

a = 10, b = 3.140000, c = Hello

可以看到,sscanf函数按照指定的格式从字符串中解析出了整数、浮点数和字符串,并赋值给相应的变量。

sprintf

sprintf函数是C语言中的一个输出函数,用于将格式化的数据写入字符串中。它的函数原型为:

int sprintf(char* str, const char* format, ...);

其中,str是要写入的字符串,format是格式化字符串,...表示可变参数列表,表示要写入的数据。

sprintf函数根据format中的格式指定符,将可变参数列表中的数据按照指定格式写入到str中。它可以写入各种类型的数据,比如整数、浮点数、字符串等。

下面是一个使用sprintf函数的例子:

#include <stdio.h>

int main() {
    char str[50];
    int a = 10;
    float b = 3.14;
    char c[] = "Hello";
    
    sprintf(str, "a = %d, b = %f, c = %s", a, b, c);
    
    printf("str = %s\n", str);
    
    return 0;
}

输出结果为:

str = a = 10, b = 3.140000, c = Hello

可以看到,sprintf函数根据指定的格式将整数、浮点数和字符串转换为字符串,并写入到str中。最终str中包含了格式化后的数据。

在这里插入图片描述
fscanf 必须和 fprintf 的格式相同,不然会出现读入错误的问题
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.3标准的数据拷贝

在这里插入图片描述

六、 文件的随机读写

在文件中,每读取一个字符,文件中的光标都会向后移动一位

在这里插入图片描述

6.1 fseek

根据文件指针的位置和偏移量来定位文件指针。

int fseek ( FILE * stream, long int offset, int origin ); 

设置的指针的位置是 起始位置 + 偏移量 ;

其中的 int origin参数就是 起始位置 , 有以下三种选择 :
文件头 SEEK_SET 0
当前位置 SEEK_CUR 1
文件尾 SEEK_END 2
long offset 偏移量参数 , 可以为正数 , 也可以为负数 ;

如果执行成功 , 则返回 0 , 失败返回非 0 , 并设置 error 错误代码 ;
例子:

/* fseek example */
#include <stdio.h>
int main ()
{
	FILE * pFile;
	pFile = fopen ( "example.txt" , "wb" );
	fputs ( "This is an apple." , pFile );
	fseek ( pFile , 9 , SEEK_SET );
	fputs ( " sam" , pFile );
	fclose ( pFile );
	return 0;
}

6.2 ftell

返回文件指针相对于起始位置的偏移量

long int ftell ( FILE * stream );

例子:

/* ftell example : getting size of a file */
#include <stdio.h>
int main ()
{
	FILE * pFile;
	long size;
	pFile = fopen ("myfile.txt","rb");
	if (pFile==NULL)
		perror ("Error opening file");
	else
	{
		fseek (pFile, 0, SEEK_END); // non-portable
		size=ftell (pFile);
		fclose (pFile);
		printf ("Size of myfile.txt: %ld bytes.\n",size);
	}
	return 0;
}

6.3 rewind

让文件指针的位置回到文件的起始位置

void rewind ( FILE * stream );

例子:

/* rewind example */
#include <stdio.h>
int main ()
{
	int n;
	FILE * pFile;
	char buffer [27];
	pFile = fopen ("myfile.txt","w+");
	for ( n='A' ; n<='Z' ; n++)
		fputc ( n, pFile);
	rewind (pFile);
	fread (buffer,1,26,pFile);
	fclose (pFile);
	buffer[26]='\0';
	printf(buffer);
	return 0;
}

七、 文件读取结束的判定

在这里插入图片描述

fgetc

在C语言中,fgetc函数用于从文件中读取一个字符。它接受一个文件指针作为参数,并返回文件中的下一个字符(以整数形式)。如果到达文件末尾或发生错误,它返回特殊值EOF(-1)。以下是一个使用示例:

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r"); // 以只读方式打开文件
    int c;

    if (file == NULL) {
        printf("无法打开文件。");
        return 1;
    }

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

    fclose(file); // 关闭文件

    return 0;
}

在这个示例中,程序打开了一个名为"example.txt"的文件,并逐个字符读取其内容,直到达到文件末尾。然后,将每个字符打印到控制台上。最后,关闭文件以释放系统资源。

fgets

在C语言中,fgets函数用于从文件中读取一行文本。它接受三个参数:一个字符数组指针,用于存储读取的字符串;一个整数值,表示要读取的最大字符数(包括空字符);一个文件指针,指定要从中读取的文件。

fgets函数会一直读取字符,直到遇到换行符(包括换行符在内)或达到指定的最大字符数。读取的字符串将存储在指定的字符数组中,并在结束时自动添加一个空字符。

以下是一个使用示例:

#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "r"); // 以只读方式打开文件
    char buffer[100];

    if (file == NULL) {
        printf("无法打开文件。");
        return 1;
    }

    while (fgets(buffer, sizeof(buffer), file) != NULL) {
        printf("%s", buffer);
    }

    fclose(file); // 关闭文件

    return 0;
}

在这个示例中,程序打开了一个名为"example.txt"的文件,并逐行读取其内容。读取的每一行被存储在名为buffer的字符数组中,并随后被打印到控制台上。最后,关闭文件以释放系统资源。

7.1 被错误使用的 feof

牢记:在文件读取过程中,不能用feof函数的返回值直接来判断文件的是否结束。

feof 的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件尾结束。

  1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )
    例如:
  • fgetc 判断是否为 EOF .
  • fgets 判断返回值是否为 NULL .
  1. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

例如:

  • fread判断返回值是否小于实际要读的个数。

文本文件的例子:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	int c; // 注意:int,非char,要求处理EOF
	FILE* fp = fopen("test.txt", "r");
	if(!fp) {
		perror("File opening failed");
	return EXIT_FAILURE;
	}
	//fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
	while ((c = fgetc(fp)) != EOF) // 标准C I/O读取文件循环
	{
		putchar(c);
	}
	//判断是什么原因结束的
	if (ferror(fp))
		puts("I/O error when reading");
	else if (feof(fp))
		puts("End of file reached successfully");
	fclose(fp);
}

二进制文件的例子:

#include <stdio.h>
enum { SIZE = 5 };
int main(void)
{
	double a[SIZE] = {1.,2.,3.,4.,5.};
	FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
	fwrite(a, sizeof *a, SIZE, fp); // 写 double 的数组
	fclose(fp);
	double b[SIZE];
	fp = fopen("test.bin","rb");
	size_t ret_code = fread(b, sizeof *b, SIZE, fp); // 读 double 的数组
	if(ret_code == SIZE) {
	puts("Array read successfully, contents: ");
	for(int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
		putchar('\n');
	} else { // error handling
	if (feof(fp))
		printf("Error reading test.bin: unexpected end of file\n");
	else if (ferror(fp)) {
	perror("Error reading test.bin");
		}
	}
	fclose(fp);
}

八、 文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的。
在这里插入图片描述fflush 刷新缓存区 写入的数据放入缓冲区,放满了才能写,或者刷新缓冲区也能写

#include <stdio.h>
#include <windows.h>
//VS2019 WIN11环境测试
int main()
{
	FILE*pf = fopen("test.txt", "w");
	fputs("abcdef", pf); //先将代码放在输出缓冲区
	printf("睡眠10秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf); //刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
	//注:fflush 在高版本的VS上不能使用了
	printf("再睡眠10秒-此时,再次打开test.txt文件,文件有内容了\n");
	Sleep(10000);
	fclose(pf);
	//注:fclose在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

这里可以得出一个结论:

因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。

如果不做,可能导致读写文件的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/336064.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flink处理函数(2)—— 按键分区处理函数

按键分区处理函数&#xff08;KeyedProcessFunction&#xff09;&#xff1a;先进行分区&#xff0c;然后定义处理操作 1.定时器&#xff08;Timer&#xff09;和定时服务&#xff08;TimerService&#xff09; 定时器&#xff08;timers&#xff09;是处理函数中进行时间相关…

pytorch(二)梯度下降算法

文章目录 优化问题梯度下降随机梯度下降 在线性模型训练的时候&#xff0c;一开始并不知道w的最优值是什么&#xff0c;可以使用一个随机值来作为w的初始值&#xff0c;使用一定的算法来对w进行更新 优化问题 寻找使得目标函数最优的权重组合的问题就是优化问题 梯度下降 通…

【极问系列】springBoot集成elasticsearch出现Unable to parse response body for Response

【极问系列】 springBoot集成elasticsearch出现Unable to parse response body for Response 如何解决&#xff1f; 一.问题 #springboot集成elasticsearch组件,进行增删改操作的时候报异常Unable to parse response body for Response{requestLineDELETE /aurora-20240120/…

编译和链接(翻译环境:预编译+编译+汇编+链接​、运行环境)

一、翻译环境和运行环境​ 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。​ 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。​ 第2种是执行环境&#xff0c;它用于实际执行代码。​ VS中编译器&#xff1a;cl.exe &#xff1b;Linux中…

基于无人机的消防灭火系统设计

摘要&#xff1a;人类社会的进步&#xff0c;使火灾变得更加频繁且越来越复杂&#xff0c;随着这些年无人机技术的发展&#xff0c;将无人机技术融入消防灭火逐渐变成必然。消防救援采用无人机主要有以下几点原因&#xff1a;一、对火场及火场周围环境信息十分匮乏&#xff0c;…

LaTeX-OCR安装教程

一. 通用安装步骤 1.前置应用 安装LaTeX-OCR首先需要安装Python。在系统自带的应用商店Microsoft Store搜索Python&#xff0c;点击最新版本Python 3.12下载即可。 2.运行powershell Win11按底部状态栏windows徽标在搜索框内搜索 powershell 或者按快捷键 “win 键 R” &am…

Hack The Box-Sherlocks-Tracer

靶场介绍 A junior SOC analyst on duty has reported multiple alerts indicating the presence of PsExec on a workstation. They verified the alerts and escalated the alerts to tier II. As an Incident responder you triaged the endpoint for artefacts of interest…

毫米波雷达4D点云生成(基于实测数据)

本期文章分享TI毫米波雷达实测4D点云生成的代码&#xff0c;包含距离、速度、水平角度、俯仰角度&#xff0c;可用于日常学习。 处理流程包含&#xff1a;数据读取和解析、MTI、距离估计、速度估计、非相干累积、2D-CFAR、水平角估计、俯仰角估计、点云生成、坐标转换等内容。…

用MATLAB函数在图表中建立模型

本节介绍如何使用Stateflow图表创建模型&#xff0c;该图表调用两个MATLAB函数meanstats和stdevstats。meanstats计算平均值&#xff0c;stdevstats计算vals中值的标准偏差&#xff0c;并将它们分别输出到Stateflow数据平均值和stdev。 请遵循以下步骤&#xff1a; 1.使用以下…

04 SpringBoot整合Druid/MyBatis/事务/AOP+打包项目

整合Druid 项目结构&#xff1a; 引入依赖&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaL…

Docker 部署考核

Docker安装 安装必要的系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2 添加docker-ce安装源&#xff1a; yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 配置阿里云Docker Yum源: yum-config-manager --ad…

4个值得使用的免费爬虫工具

在信息时代&#xff0c;数据的获取对于各行业都至关重要。而在数据采集的众多工具中&#xff0c;免费的爬虫软件成为许多用户的首选。本文将专心分享四款免费爬虫工具&#xff0c;突出介绍其中之一——147采集软件&#xff0c;为您揭示这些工具的优势和应用&#xff0c;助您在数…

使用 crypto-js 进行 AES 加解密操作

在前端开发中&#xff0c;数据的加密和解密是为了保障用户隐私和数据的安全性而常见的任务。AES&#xff08;Advanced Encryption Standard&#xff09;是一种对称密钥加密算法&#xff0c;被广泛用于保护敏感信息的传输和存储。本文将介绍 AES 加解密的基本原理&#xff0c;并…

超过GPT3.5?Mixtral 8*7B 模型结构分析

Datawhale干货 作者&#xff1a;宋志学&#xff0c;Datawhale成员 前言 2023年12月11日&#xff0c;Mistral AI团队发布了一款高质量的稀疏专家混合模型Mixtral 8x7B。 Mistral AI继续致力于向开发者社区提供最优秀的开放模型。在人工智能领域向前发展&#xff0c;需要采取超越…

Node.js 使用 cors 中间件解决跨域问题

CORS 跨域资源共享 什么是 CORS cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件&#xff0c;可以很方便地解决跨域问题。 CORS &#xff08;Cross-Origin Resource Sharing&#xff0c;跨域资源共享&#xff09;由一系列 HTTP 响应头组成&#xff0c;这些…

统计学-R语言-4.6

文章目录 前言列联表条形图及其变种---单式条形图条形图及其变种---帕累托图条形图及其变种---复式条形图条形图及其变种---脊形图条形图及其变种---马赛克图饼图及其变种---饼图饼图及其变种---扇形图直方图茎叶图箱线图小提琴图气泡图总结 前言 本篇文章是对数据可视化的补充…

Vulnhub-TECH_SUPP0RT: 1渗透

文章目录 一、前言1、靶机ip配置2、渗透目标3、渗透概括 开始实战一、信息获取二、使用smb服务获取信息三、密码破解四、获取webshell五、反弹shell六、web配置文件获取信息七、提权 一、前言 由于在做靶机的时候&#xff0c;涉及到的渗透思路是非常的广泛&#xff0c;所以在写…

手把手教你如何快速定位bug,如何编写测试用例,快来观摩......

手把手教你如何快速定位bug,如何编写测试用例,快来观摩......手把手教你如何快速定位bug,如何编写测试用例,快来观摩......作为一名测试人员如果连常见的系统问题都不知道如何分析&#xff0c;频繁将前端人员问题指派给后端人员&#xff0c;后端人员问题指派给前端人员&#xf…

Rust - 可变引用和悬垂引用

可变引用 在上一篇文章中&#xff0c;我们提到了借用的概念&#xff0c;将获取引用作为函数参数称为 借用&#xff08;borrowing&#xff09;&#xff0c;通常情况下&#xff0c;我们无法修改借来的变量&#xff0c;但是可以通过可变引用实现修改借来的变量。代码示例如下&…

超详细的 pytest 钩子函数 —— 之初始钩子和引导钩子来啦!

前几篇文章介绍了 pytest 点的基本使用&#xff0c;学完前面几篇的内容基本上就可以满足工作中编写用例和进行自动化测试的需求。从这篇文章开始会陆续给大家介绍 pytest 中的钩子函数&#xff0c;插件开发等等。 仔细去看过 pytest 文档的小伙伴&#xff0c;应该都有发现 pyt…