【文件操作:解锁高效读写与管理技巧】

本章重点

  • 为什么使用文件

  • 什么是文件

  • 文件的打开和关闭

  • 文件的顺序读写

  • 文件的随机读写

  • 文本文件和二进制文件

  • 文件读取结束的判定

  • 文件缓冲区

1. 为什么使用文件

        C语言中的变量和数据通常只在程序运行时存在于内存中,一旦程序结束,这些数据就会被销毁。通过将数据写入文件,可以实现数据的持久性存储,使得数据在程序运行结束后仍然存在,可以在下次程序运行时读取和使用。这意味着它们可以保存在磁盘或其他持久性存储媒介上,并且在计算机重新启动后仍然存在。

2. 什么是文件

磁盘上的文件是文件。

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

2.1 程序文件

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

2.2 数据文件

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

本章讨论的是数据文件。 在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。其实有时候我们会把信息输出到磁盘上,当需要的时候再从磁盘上把数据读取到内存中使用,这里处理的就是磁盘上文件。

2.3 文件名

  • 一个文件要有一个唯一的文件标识,以便用户识别和引用。
  • 文件名包含3部分:文件路径+文件名主干+文件后缀
  • 例如: c:\code\test.txt
  • 为了方便起见,文件标识常被称为文件名

3. 文件的打开和关闭

3.1 文件指针

  • 缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
  • 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是由系统声明的,取名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指向某个文件的文件信息区(是一个结构体变 量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件。

【文件操作:解锁高效读写与管理技巧】_第1张图片

3.2 文件的打开和关闭 

  • 文件在读写之前应该先打开文件,在使用结束之后应该关闭文件。
  • 在编写程序的时候,在打开文件的同时,都会返回一个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系。
  • ANSIC 规定使用fopen函数来打开文件,fclose来关闭文件。
//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );

打开方式如下:

文件使用方式 含义 如果指定文件不存在
“r”(只读) 为了输入数据,打开一个已经存在的文本文件 出错
“w”(只写) 为了输出数据,打开一个文本文件 建立一个新的文件
“a”(追加) 向文本文件尾添加数据 建立一个新的文件
“rb”(只读) 为了输入数据,打开一个二进制文件 出错
“wb”(只写) 为了输出数据,打开一个二进制文件 建立一个新的文件
“ab”(追加) 向一个二进制文件尾添加数据 建立一个新的文件
“r+”(读写) 为了读和写,打开一个文本文件 出错
“w+”(读写) 为了读和写,新建一个新的文件 建立一个新的文件
“a+”(读写) 打开一个文件,在文件尾进行读写 建立一个新的文件
“rb+”(读写) 为了读和写打开一个二进制文件 出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件 建立一个新的文件
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写 建立一个新的文件
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");//当前目录下没有该文件
    //打开文件
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
     //关闭文件
	fclose(pf);
    pf = NULL;
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第2张图片

4. 文件的顺序读写

4.1 顺序读写函数介绍

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

fputc

int fputc ( int character, FILE * stream );
  • 将字符写入流并推进位置指示器。

  • 该字符被写入流的内部位置指示器所指示的位置,然后自动向前推进一个位置。

  • character

  • 要写入的字符的 int 提升值。 在写入时,该值在内部被转换为无符号 char。

  • stream

  • 指向标识输出流的 FILE 对象的指针。

#include
int main()
{
	//打开文件
	//使用'w'的方式,没有文件的时候新建文件
	FILE* pf = fopen("data.txt", "w");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('1', pf);
	fputc('2', pf);
	fputc('3', pf);
	//关闭文件
	fclose(pf);
	return 0;
}

文件所在位置:

【文件操作:解锁高效读写与管理技巧】_第3张图片运行结果:

【文件操作:解锁高效读写与管理技巧】_第4张图片

如何写到终端上去呢?

#include
int main()
{
	//打开文件
	//使用'w'的方式,没有文件的时候新建文件
	FILE* pf = fopen("data.txt", "w");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	fputc('1', stdout);
	fputc('2', stdout);
	fputc('3', stdout);
	//关闭文件
	fclose(pf);
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第5张图片

那如何读文件呢?

#include
int main()
{
	//打开文件
	//使用'r'的方式,没有文件的时候会报错
	FILE* pf = fopen("data.txt", "r");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//读文件 - 文件末尾或读取失败 - 返回EOF
	int ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	ch = fgetc(pf);
	printf("%c", ch);
	//关闭文件
	fclose(pf);
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第6张图片

那如何从键盘上读呢?

fgetc

int fgetc ( FILE * stream );
  • 返回指定流的内部文件位置指示器当前指向的字符。然后,内部文件位置指示器会向下一个字符推进。

  • 如果在调用时流已经到达文件末尾,该函数返回 EOF 并为该流设置文件末尾指示器 (feof)。

  • 如果发生读取错误,该函数返回 EOF 并为该流设置错误指示器 (ferror)。

  • fgetc 和 getc 是等价的,但在某些库中 getc 可能被实现为宏。

  • stream

  • 指向标识输出流的 FILE 对象的指针。

#include
int main()
{
	//打开文件
	//使用'r'的方式,没有文件的时候会报错
	FILE* pf = fopen("data.txt", "r");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//读文件 - 文件末尾或读取失败 - 返回EOF
	int ch = fgetc(stdin);
	printf("%c", ch);
	ch = fgetc(stdin);
	printf("%c", ch);
	ch = fgetc(stdin);
	printf("%c", ch);
	//关闭文件
	fclose(pf);
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第7张图片参考图:

【文件操作:解锁高效读写与管理技巧】_第8张图片

那么再来写练一下文本行输出函数

int fputs ( const char * str, FILE * stream );
  • 将由 str 指向的 C 字符串写入流中。

  • 该函数从指定地址(str)开始复制,直到遇到终止空字符('\0')为止。终止空字符不会被复制到流中。

  • 注意,fputs 与 puts 的区别不仅在于可以指定目标流,而且 fputs 不会写入额外的字符,而 puts 会自动在末尾追加换行字符。

#include
int main()
{
	FILE* pf = fopen("data.txt","w");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件 - 写一行 - 输出
	//int fputs ( const char * str, FILE * stream );
	fputs("hello world\n",pf);
	fputs("big dream\n", pf);
	//这两句话在文件中是一行还是两行? 两行

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

【文件操作:解锁高效读写与管理技巧】_第9张图片

文本输入函数: 

int fgetc ( FILE * stream );
  • 返回指定流的内部文件位置指示器当前指向的字符。然后,内部文件位置指示器会向下一个字符推进。

  • 如果在调用时流已经到达文件末尾,该函数返回 EOF 并为该流设置文件末尾指示器 (feof)。

  • 如果发生读取错误,该函数返回 EOF 并为该流设置错误指示器 (ferror)。

  • fgetc 和 getc 是等价的,但在某些库中 getc 可能被实现为宏。

#include
int main()
{
	FILE* pf = fopen("data.txt","r");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//读文件 - 读一行 - 输入
	//char * fgets ( char * str, int num, FILE * stream );
	char arr[10] = { 0 };
	fgets(arr, 10, pf);
	printf("%s\n", arr);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第10张图片

        上面的结果发现输出了两个换行,说明fgets在获取到num-1(留一个位置给'\n')个字符之后会自动换行。如果该字符没有读完,下次读取就会取消第一次读取的换行,将第二次的输出字符,直至这个字符串读完再换行。

格式输出函数:

int fprintf ( FILE * stream, const char * format, ... );
  • 将由 format 指向的 C 字符串写入流中。如果 format 包含格式说明符(以 % 开头的子序列),则会格式化并插入 format 后面的附加参数,替换相应的格式说明符。

  • 在 format 参数之后,该函数期望至少有与 format 指定的格式相同数量的附加参数。

#include
int main()
{
	FILE* pf = fopen("data.txt","w");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件  - 输出
	//int fprintf ( FILE * stream, const char * format, ... );
	fprintf(pf,"%d %lf",520,13.14);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

结果:

【文件操作:解锁高效读写与管理技巧】_第11张图片

格式输入函数

int fscanf ( FILE * stream, const char * format, ... );
  • 从流中读取数据,并根据参数 format 将它们存储到附加参数所指向的位置。

  • 附加参数应该指向已经分配的对象,其类型由 format 字符串中对应的格式说明符指定。

#include
int main()
{
	int a;
	double d;
	FILE* pf = fopen("data.txt","r");
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//读文件  - 输入
	//int fscanf ( FILE * stream, const char * format, ... );
	fscanf(pf, "%d %lf", &a, &d);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

二进制输出:

size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
  • 将由 ptr 指向的内存块中的 count 个元素写入流的当前位置。

  • 流的位置指示器会根据写入的总字节数向前推进。

  • 在内部,该函数将 ptr 指向的块解释为类型为 unsigned char 的 (size*count) 个元素的数组,并按顺序将它们写入流中,就像对每个字节调用了 fputc 一样。

#include
struct S
{
	int a;
	double d;
};
int main()
{
	struct S s = { 520,13.14 };
	FILE* pf = fopen("data.txt", "wb");//二进制打开文件
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//写文件 - 输出
	//size_t fwrite(const void* ptr, size_t size, size_t count, FILE * stream);
	fwrite(&s, sizeof(struct S), 1, pf);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出:二级制文件

【文件操作:解锁高效读写与管理技巧】_第12张图片

 二进制输入:

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
  • 从流中读取 count 个元素,每个元素大小为 size 字节,并将它们存储在由 ptr 指定的内存块中。

  • 流的位置指示器会根据成功读取的总字节数向前推进。

  • 如果成功读取,总共读取的字节数为 (size*count)。

#include
struct S
{
	int a;
	double d;
};
int main()
{
	struct S s = { 0 };
	FILE* pf = fopen("data.txt", "rb");//二进制读出文件
	if (!pf)
	{
		perror("fopen");
		return 1;
	}
	//读文件 - 输入
	//size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
	fread(&s, sizeof(struct S), 1, pf);
	printf("%d %lf", s.a, s.d);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第13张图片

4.2 对比一组函数:

int sscanf ( const char * s, const char * format, ...);
  • 从字符串 s 中读取数据,并根据参数 format 将其存储到附加参数所指向的位置,就像使用 scanf 一样,但是从字符串 s 而不是标准输入(stdin)读取。

  • 附加参数应该指向已经分配的对象,其类型由 format 字符串中对应的格式说明符指定。

int sprintf ( char * str, const char * format, ... );
  • 该函数将以与使用 printf 时相同的文本组成一个字符串,但不会将其打印,而是将内容存储为 C 字符串,存放在由 str 指向的缓冲区中。

  • 缓冲区的大小应足够大,以容纳整个结果字符串(请参阅更安全的版本 snprintf)。

  • 内容后会自动追加一个终止空字符。

  • 在 format 参数之后,该函数期望至少有足够的附加参数以满足 format 的需求。

#include
struct S
{
	int a;
	double d;
	char arr[10];
};
int main()
{
	struct S s = { 12,3.14,"hello" };
	char str[100];
	struct S temp = { 0 };
	//int sprintf ( char * str, const char * format, ... );
	sprintf(str, "%d %lf %s", s.a, s.d, s.arr);
	printf(str);

	//int sscanf ( const char * s, const char * format, ...);
	sscanf(str, "%d %lf %s", &(temp.a), &(temp.d), &(temp.arr));
	printf("%d %lf %s\n", temp.a, temp.d, temp.arr);
	return 0;
}
  1. scanf, fscanf, sscanf:

    • scanf: 从标准输入(键盘)读取数据,并根据指定的格式进行解析,将数据存储到给定的变量中。
    • fscanf: 从指定的文件流中读取数据,并根据指定的格式进行解析,将数据存储到给定的变量中。
    • sscanf: 从指定的字符串中读取数据,并根据指定的格式进行解析,将数据存储到给定的变量中。
  2. printf, fprintf, sprintf:

    • printf: 将格式化的数据输出到标准输出(屏幕)。
    • fprintf: 将格式化的数据输出到指定的文件流。
    • sprintf: 将格式化的数据输出到指定的字符串缓冲区。

总结区别:

  • scanf, fscanf, sscanf 主要用于输入操作,从不同来源(标准输入、文件、字符串)读取数据。
  • printf, fprintf, sprintf 主要用于输出操作,将格式化的数据输出到不同的目标(标准输出、文件、字符串缓冲区)中。

5. 文件的随机读写

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

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

stream:指向标识流的 FILE 对象的指针。

offset:

  • 二进制文件:从 origin 确定的位置偏移的字节数。
  • 文本文件:要么为零,要么为 ftell函数 返回的值。

origin:用作偏移参考的位置。

【文件操作:解锁高效读写与管理技巧】_第14张图片

  •  SEEK_SET 文件开头
  • SEEK_CUR 文件指针的当前位置
  • SEEK_END 文件末尾 
#include 
int main()
{
	FILE* pFile;
	pFile = fopen("example.txt", "wb");
	fputs("This is an apple.", pFile);///写文件 - 输出
	fseek(pFile, 9, SEEK_SET);//从起始位置开始偏移
	//fseek(pFile, -8, SEEK_END);//从起始位置开始偏移
	//偏移量为9的字符串换为 sam
	fputs(" sam", pFile);///写文件 - 输出
	fclose(pFile);
	pFile = NULL;
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第15张图片

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

long int ftell ( FILE * stream );
  • 对于二进制流,这是从文件开头算起的字节数。
  • 对于文本流,数值可能没有实际意义,但仍可用于稍后使用 fseek 将位置恢复到相同位置(如果还有使用 ungetc 放回的字符待读取,则行为是未定义的)。
#include 
int main()
{
    FILE* pFile;
    long size;
    pFile = fopen("example.txt", "rb");
    if (pFile == NULL) 
        perror("Error opening file");
    else
    {
        fseek(pFile, 0, SEEK_END);   //文件末尾 
        size = ftell(pFile);
        fclose(pFile);
        pFile = NULL;
        printf("Size of example.txt: %ld bytes.\n", size);
    }
    return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第16张图片

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

void rewind ( FILE * stream );
  • 该函数将与流关联的位置指示器设置为文件的开头。
  • 成功调用此函数后,流相关的文件末尾和错误内部指示器将被清除,并且该流上先前调用 ungetc 的所有影响都将被丢弃。
  • 对于以更新模式打开(读写模式)的流,调用 rewind 允许在读和写之间切换。换句话说,它可以重新定位流的位置指示器到文件的开头,以便可以从头开始读取或写入数据。
#include 
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);
    pFile = NULL;
    buffer[26] = '\0';
    puts(buffer);
    return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第17张图片

6. 文本文件和二进制文件

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

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

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

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

  • 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

  • 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符一个字节),而二进制形式输出,则在磁盘上只占4个字节(VS2013测试十六进制:10 27 00 00)。

【文件操作:解锁高效读写与管理技巧】_第18张图片

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

运行结果:

【文件操作:解锁高效读写与管理技巧】_第19张图片

解读该文件:

【文件操作:解锁高效读写与管理技巧】_第20张图片

 【文件操作:解锁高效读写与管理技巧】_第21张图片

 【文件操作:解锁高效读写与管理技巧】_第22张图片

 【文件操作:解锁高效读写与管理技巧】_第23张图片

7. 文件读取结束的判定

7.1 被错误使用的feof

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

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

1. 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )

例如:

  • fgetc 判断是否为 EOF .
  • fgets 判断返回值是否为 NULL .

2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。

例如:

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

文本文件的例子:

#include 
#include 
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);
    fp = NULL;
    return 0;
}

二进制文件的例子:

#include 
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);
    fp = NULL;
    return 0;
}

文件拷贝(代码形式)

//data.txt的内容拷贝到data1.txt
#include
int main()
{
	FILE* pfRead = fopen("data.txt","r");
	if (!pfRead)
	{
		perror("open file for read");
		return 1;
	}
	FILE* pfWrite = fopen("data1.txt", "w");
	if (!pfWrite)
	{
		perror("open file for write");
		fclose(pfRead);
		pfRead = NULL;
		return 1;
	}
	//读写文件 vb
	int ch = 0;
	while ((ch = fgetc(pfRead)) != EOF)
	{
		fputc(ch, pfWrite);
	}
	//关闭文件
	fclose(pfWrite);
	pfWrite = NULL;

	fclose(pfRead);
	pfRead = NULL;
	return 0;
}

运行结果:

【文件操作:解锁高效读写与管理技巧】_第24张图片

8. 文件缓冲区

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

【文件操作:解锁高效读写与管理技巧】_第25张图片

fflush - 清空缓冲区,缓冲区写入文件

int fflush ( FILE * stream )
  • 如果给定的流是以写入方式打开的(或者以更新方式打开且最后一个 I/O 操作是输出操作),则会将其输出缓冲区中的所有未写数据写入文件。
  • 如果流是空指针,则会刷新所有这样的流。
  • 在其他所有情况下,行为取决于特定的库实现。在某些实现中,刷新以读取方式打开的流会导致其输入缓冲区被清空。
  • 调用此函数后,流仍然保持打开状态。
  • 当关闭文件时,无论是因为调用 fclose 还是因为程序终止,与其关联的所有缓冲区都会自动刷新。
#include 
#include 
//VS2019 WIN10环境测试
int main()
{
	FILE* pf = fopen("test1.txt", "w");
	fputs("abcdef", pf);//先将代码放在输出缓冲区
	printf("睡眠10秒-已经写数据了,打开test1.txt文件,发现文件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区时,才将出输缓冲区的数据写到文件(磁盘)
	//注:fflush 在高版本的VS上不能使用了
	printf("再睡眠10秒-此时,再次打开test1.txt文件,文件有内容了\n");
	Sleep(10000);
	fclose(pf);
	//注:fclose在关闭文件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

结论:

  • 因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。
  • 如果不做,可能导致读写文件的问题。

本章结束啦!!!

你可能感兴趣的:(进阶C语言,c语言)