当前位置: 首页 > news >正文

详解文件操作

一、前言:

1.什么是文件?

        在计算机领域,文件(File) 是指存储在外部存储介质(如硬盘、U 盘、SSD 等)上的、由一组相关数据或信息组成的集合,它是计算机长期保存数据的基本单位。

        简单来说,文件就是 “存放在磁盘上的一段有意义的数据”,比如我们日常接触的.txt文档、.jpg图片、.exe程序、.c源代码等,本质上都是不同类型的文件。

        

2.文件的分类?

        在 C 语言文件操作中,最核心的分类是 “文本文件” 和 “二进制文件”,二者的存储方式和处理逻辑完全不同,是学习 C 语言文件操作的基础。

        

分类存储原理可读性优点典型用途
文本文件数据以ASCII 码(或 Unicode)字符的形式存储,每个字符对应一个字节(如数字123会存为'1''2''3'三个字符的 ASCII 码)。人类可读(用记事本打开可直接看到内容)直观、易调试、跨平台性好日志文件、配置文件、源代码(.c/.h
二进制文件数据以计算机原生的二进制格式存储(如数字123直接存为二进制01111011,占 1 个字节)。人类不可读(用记事本打开是乱码)存储效率高、读写速度快、能保存复杂数据(如结构体、图像)图片(.jpg)、音频(.mp3)、可执行程序(.exe)、数据库文件

        

3.文件名

⼀个⽂件要有⼀个唯⼀的⽂件标识,以便用户识别和引⽤。通常来说⽂件名包含3部分:⽂件路径+⽂件名主⼲+⽂件后缀
例如: c:\code\test.txt
为了⽅便起⻅,⽂件标识常被称为⽂件名。

        

4.为什么要使用文件?

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

        

5.流(Stream)与文件指针

关于流的知识:

①流(Stream)代表了数据的流动方式,是对输入 / 输出源(如文件、网络、内存)的抽象表示。它将数据的读取 / 写入过程抽象为一连串的字节序列传输,屏蔽了底层设备的差异(比如文件和网络的操作接口不同,但通过流可以统一处理)。

        

②C 语言并没有直接操作 “磁盘文件”,而是通过文件流(File Stream) 这一抽象概念间接操作 —— 流是 “数据在程序与文件之间传输的通道”。

        

③三个标准流:C程序默认在程序启动时,为我们默认打开。

        

• stdin-标准输⼊流:在大多数的环境中从键盘输入,scanf函数就是从标准输入流中读取数据。

        

 stdout-标准输出流:大多数的环境中输出至显示器界面,printf函数就是将信息输出到标准输出 流中。

        

• stderr-标准错误流:大多数环境中输出到显示器界面。

        

 关于文件指针:      

①每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息(如⽂件的名字,⽂件状态及⽂件当前的位置等), 这些信息是保存在⼀个结构体变量中的。

//该结构体类型是由系统声明的,取名 FILE.
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是 C 标准库定义的一个结构体类型(在<stdio.h>中声明),内部包含了文件的状态(如读写位置、打开模式、错误标记等)。
  • FILE*(文件指针)指向这个FILE结构体变量,程序通过操作文件指针,就能间接控制文件的打开、读写、关闭等所有操作。

        

如下图所示:

        

        

 两者的关联:            

简单类比:

        可以把流想象成一条河流(数据的流动通道),而文件指针是河上的 “标记浮标”,标记着当前正在操作的位置,我们通过浮标确定从哪里取水(读取)或倒水(写入),操作后浮标会自动移动,也可以手动调整浮标的位置。

        

总结来说:流是数据传输的通道,文件指针是这个通道中标记当前操作位置的工具,二者配合实现了对数据的有序读写。

        

如下图所示:通过C程序控制流,来对文件进行 输入/ 输出 操作。

        

二、文件的打开与关闭

1.文件的打开

①fopen函数的简介:

               

        FILE * fopen ( const char * filename, const char * mode );

        

参数一 const char * filename:文件指针

        

参数二 const char * mode:打开方式

通过以下模式进行打开:

"r"read: Open file for input operations. The file must exist.
read: 用于输入操作打开文件。该文件必须存在。
"w"write: Create an empty file for output operations. If a file with the same name already exists, its contents are discarded and the file is treated as a new empty file.
write: 创建一个空文件用于输出操作。如果已存在同名文件,其内容将被丢弃,该文件被视为一个新空文件。
"a"append: Open file for output at the end of a file. Output operations always write data at the end of the file, expanding it. Repositioning operations (fseek, fsetpos, rewind) are ignored. The file is created if it does not exist.
append: 在文件末尾打开文件用于输出。输出操作总是在文件末尾写入数据,扩展文件。重定位操作(fseek, fsetpos, rewind)被忽略。如果文件不存在,则创建该文件。
"r+"read/update: Open a file for update (both for input and output). The file must exist.
read/update: 用于更新(输入和输出)打开文件。该文件必须存在。
"w+"write/update: Create an empty file and open it for update (both for input and output). If a file with the same name already exists its contents are discarded and the file is treated as a new empty file.
写入/更新:创建一个空文件并打开它进行更新(既可用于输入也可用于输出)。如果已存在同名的文件,其内容将被丢弃,该文件被视为一个新创建的空文件。
"a+"append/update: Open a file for update (both for input and output) with all output operations writing data at the end of the file. Repositioning operations (fseek, fsetpos, rewind) affects the next input operations, but output operations move the position back to the end of file. The file is created if it does not exist.
追加/更新:打开一个文件进行更新(既可用于输入也可用于输出),所有输出操作都在文件末尾写入数据。定位操作(fseek, fsetpos, rewind)会影响下一次输入操作,但输出操作会将位置重置到文件末尾。如果文件不存在,则会创建该文件。

            

2.文件的关闭    

①fclose:

        

        int fclose ( FILE * stream );

        

核心功能:打开文件

        

参数 FILE * stream:文件指针

        

3.代码演示:文件的开、关

        通过以写的方式文件 “test.txt”

#include<stdio.h>int  main( )
{			//打开文件FILE* pf = fopen("test.txt", "w");//打开成功,返回文件信息区的地址//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//关闭文件fclose(pf);//避免野指针,主动将pf置空处理pf = NULL;return 0;
}

        

三、文件的顺序读写

1.fgetc / fputc 函数

①fputc 函数的简介 :

        

                int fputc ( int character, FILE * stream );

核心功能:将单个字符写入文件中(输出流)

        

参数一 int character:单个字符 (以ASCII的形式传递)

        

参数二 FILE * stream :文件指针           

         

代码示例1:将单个字符输出到文件中

        

int  main( )
{			//打开文件FILE* pf = fopen("test.txt", "w");//打开成功,返回文件信息区的地址//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//写入文件fputc('a', pf);fputc('b', pf);fputc('c', pf);//关闭文件fclose(pf);//避免野指针,主动将pf置空处理pf = NULL;return 0;
}

        

代码示例2:通过循环将二十六个字母输出到文件中

        

int  main( )
{			//打开文件FILE* pf = fopen("test.txt", "w");//打开成功,返回文件信息区的地址//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}char ch = '0';for (ch = 'a'; ch <= 'z'; ch++){fputc(ch, pf);fputc(' ', pf);}//关闭文件fclose(pf);//避免野指针,主动将pf置空处理pf = NULL;return 0;
}

        

②fgetc 函数的简介:

        

        int fgetc ( FILE * stream );

        

核心功能:从流中获取字符

        

参数一 FILE * stream:文件指针

        

温馨提示:如果位置指示器位于文件末尾,该函数返回 EOF ,如果发生其他读取错误,该函数也会返回 EOF,所以我们可以通过其返回值是否等于EOF来判断读取是否结束。

        

思考探究:为什么fgetc函数返回类型不是 char 类型 而是 int 类型?

fgetc(pf) 是从文件流pf中读取一个字符的函数,但其返回值类型是int(整数),而非char(字符),这是关键设计:

        

        ①正常情况:当读取到有效字符时,fgetc会返回该字符的 ASCII 码值(范围是 0~255,对应unsigned char的取值范围),并被转换为int类型返回。

        

        ②特殊情况:当读取到文件末尾(EOF,End of File)或发生错误时,fgetc会返回一个特殊值EOF(通常定义为-1,是int类型)。

如果用char接收(而非int),会出现问题:

        
  •char类型通常是 8 位,若为signed char,其范围是 - 128~127。当读取到 ASCII 码为 255 的字符(如某些特殊符号)时,会被转换为 -1(与EOF的值相同),导致程序无法区分 “读取到 255 字符” 和 “读取到文件末尾”。

        
 •int是更高位数的类型(通常 32 位),可以同时容纳 0~255 的字符值和EOF(-1),避免了这种混淆。

        

代码示例1:从文件中读取一个字符

        

#include<stdio.h>int  main()
{//打开文件FILE* pf = fopen("test.txt", "r");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//读文件int ch = fgetc(pf);printf("%c", ch);//关闭文件fclose(pf);pf = NULL;return 0;
}

        

代码示例二:通过循环读取文件中的多行字符

        

#include<stdio.h>int  main()
{//打开文件FILE* pf = fopen("test.txt", "r");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//读文件int ch = 0;while ((ch = fgetc(pf)) != EOF)printf("%c", ch);//关闭文件fclose(pf);pf = NULL;return 0;
}

2.fgets / fputs 函数

①fputs的简介:

        

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

        

核心功能:将字符串写入流

        

参数一 const char * str:要写入流的 C 字符串。

        

参数二 FILE * stream:文件指针     

        

代码示例:将“hello world”输出到文件中

        

#include<stdio.h>int  main()
{//打开文件FILE* pf = fopen("test.txt", "w");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//写一串字符到文件中fputs("hello world\n", pf);//关闭文件fclose(pf);pf = NULL;return 0;
}

        

②fgets函数的简介:

        

        char * fgets ( char * str, int num, FILE * stream );

        

核心功能:从流中读取字符,并将它们作为 C 字符串存储到 str 中,直到读取了 (num-1) 个字符或遇到换行符或文件结束符,以先发生者为准。

        

参数一 char * str:是指向字符数组的指针,str指向的空间用于存储读取到的字符串。

        

参数二 int num:最大读取字符数(包含结尾的\0,实际最多读取num-1 个字符)。

        

参数三 FILE * stream:输入流的文件指针

        

温馨提示:

        ①如果读取成功则返回str,如果读取失败或则读取到文件结束,返回NULL,因此可以通过判断其返回值是否为NULL来判断其是否读取结束。

        

        ②读取成功的字符串str都会自动包含‘\0’。

        

代码示例:从文件中读取多行字符串

        

int  main()
{//打开文件FILE* pf = fopen("test.txt", "r");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}char str[100] = {0};while ((fgets(str, 10, pf)) != NULL){printf("%s", str);}//关闭文件fclose(pf);pf = NULL;return 0;
}

多行处理的核心逻辑总结:

        

        ①文件指针自动移动:每次 fgets 读完一行后,文件指针会自动定位到下一行的开头(跳过当前行的 \n),为下一次读取做好准备。

        

       ②循环迭代读取:通过 while 循环持续调用 fgets,直到返回 NULL(文件结束),确保所有行都被依次处理。

        

        ③兼容超长行:如果某一行长度超过缓冲区大小,fgets 会分多次读取该行(先读一部分,下次从剩余部分继续),读完该行后再自动处理下一行。

        

3.fscanf / fprintf 函数

①fprintf的函数简介:

        

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

        

核心功能:将格式化数据写入流

        

参数一 FILE * stream:指向FILE 对象的指针,表示要写入的文件流(如文件指针,stdout)

        

参数二 const char * format:格式化字符串,包含要写入的文本和格式说明符(如%d 、%s等)

        

参数三 ...:可变参数列表,提供与格式字符串中说明符对应的数据。

        

温馨提示:可以通过类比printf来理解这个函数,printf函数是通过标准输出流进行输出,而fprintf既可以使用标准输出流又可以使用文件流。      

        

代码示例:通过fprintf函数格式化输出结构体成员到文件中

        

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>struct S
{char name[20];int age;float score;
};int  main()
{struct S s = { 0 };//打开文件FILE* pf = fopen("test.txt", "r");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//写入文件中struct S s = {"zhang shan" , 20 , 80.0f};fprintf(pf, "%s %d %f", s.name, s.age, s.score);//关闭文件fclose(pf);pf = NULL;return 0;
}

        

②fscanf函数的简介:

        

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

        

核心功能:从流中读取格式化数据

        

参数一 FILE* stream:指向FILE 对象的指针,表示要读取的文件流(如stdin 、文件指针等)。

        

参数二 const char* format:格式化字符串,定义如何解析输⼊数据(如%d 、%f、%s等)。

        

参数三 ... :可变参数列表,提供存储数据的变量地址(需与格式字符串中的说明符匹配)。

        

温馨提示:可以通过类比scanf来理解这个函数,scanf函数是通过标准输入流进行输出,而scanf既可以使用标准输入流又可以使用文件流。 

        

代码示例:通过fscanf,从文件中输入到结构体变量中

        

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>struct S
{char name[20];int age;float score;
};int  main()
{struct S s = { 0 };//打开文件FILE* pf = fopen("test.txt", "r");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//读取文件//从text读取数据,并输出到屏幕上fscanf(pf, "%s %d %f", s.name, &(s.age), &(s.score));printf("%s %d %f", s.name, s.age, s.score);//关闭文件fclose(pf);pf = NULL;return 0;
}

        

4.fread / fwrite 函数

①fwrite函数的简介:

        

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

        

核心功能:将一个包含 count 个元素(每个元素大小为 size 字节)的数组从 ptr 指向的内存块写入流的当前位置。

        

参数一 const void * ptr:指向要写入的元素数组的指针,转换为 const void* 

        

参数二 size_t size:要写入的每个元素的字节数大小

        

参数三 size_t count:元素数量,每个元素的大小为 size 字节

        

参数四 FILE * stream:指向一个 FILE 对象的指针,该对象指定了一个输出流。

        

返回值:①成功写入的元素总数将被返回。

        

              ②如果这个数字与 count 参数不同,一个写入错误阻止了函数的完成,在这种情况下,流的错误指示器(ferror)将设置。

        

代码示例:将整形数组的元素以二进制形式输出到文件中

        

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>int  main()
{//打开文件FILE* pf = fopen("test.txt", "rb");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//以二进制形式写数据到文件中int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);fwrite(arr, sizeof(int), sz, pf);//关闭文件fclose(pf);pf = NULL;return 0;
}v

②fread函数的简介:

        

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

           

核心功能:从流中读取一个包含 count 个元素的数组,每个元素大小为 size 字节,并将它们存储在由 ptr 指定的内存块中。

        

参数一:指向至少包含 (size*count) 字节内存块的指针,转换为 void* 。

        

参数二:要读取的每个元素的大小,以字节为单位

        

参数三:元素数量,每个元素的大小为 size 字节

        

参数四:指向一个 FILE 对象的指针,该对象指定一个输入流   

        

返回值:成功读取的元素总数将被返回,如果这个数字与 count 参数不同,则说明发生了读取错误或读取时达到了文件末尾,所以可以通过返回值分析,是否读取结束。

        

代码示例:以二进制的形式从文件中,读入数据块到数组中

        

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>int  main()
{//打开文件FILE* pf = fopen("test.txt", "rb");  //注意二进制读入是“wb”//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}//以二进制形式读入到数组中int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(int);fread(arr, sizeof(int), sz, pf);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}//关闭文件fclose(pf);pf = NULL;return 0;
}

        

5.feof / ferror 函数

①feof函数的简介:

       

        int feof ( FILE * stream );

        

核心功能:检查文件结束时,是因为遇到了文件末尾。

          

参数 FILE * stream : 指向标识流的 FILE 对象的指针

        

温馨提示:

        feof不能做为文件读取结束的标志,它是用来检测文件因为遇到文件末尾而结束,此外文件结束的原因还可能是读取失败,所以不能用其来作为判断。

        

        

ferror 函数的简介:

        

        int ferror ( FILE * stream );

        

核心功能:检查错误指示器,检查文件读取结束,是否是因为文件读取失败导致的。

        

参数 FILE * stream : 指向标识流的 FILE 对象的指针

       

代码示例:当文件读取结束时,判断是什么原因结束。

        

#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>int  main( )
{	//打开文件FILE* pf = fopen("test.txt", "r");//打开失败,返回空指针if (pf == NULL){perror("fopen");return 1;}int ch = 0;while ( (ch = fgetc(pf)) != EOF){printf("%c\n", ch);}//判断是什么原因导致,读取结束if (feof(pf)){//遇到文件末尾结束printf("遇到文件末尾,正常读取结束\n");}else if(ferror(pf)){//读取的过程中发生错误。perror("fgetc");		}//关闭文件fclose(pf);pf = NULL;return 0;
}

        

feof函数的注意事项:

①feof(pf) 的作用是 检查文件流的 “EOF 标志位”:

 
  • 这个标志位默认是 假(0)
  • 只有当 读取操作(如 fgetc)尝试读取 “超出文件末尾的数据” 时(比如文件只有 10 个字符,却要读第 11 个),系统才会把这个标志位设为 真(非0)
  • feof 本身不进行 “读取”,只 “查看标志位”。

        

②错误示例:while(!feof(pf)) 

        

解析:先判断 “是否没到末尾”,再执行读取。但因为 feof 滞后,会导致 多执行一次无效的 “读取 + 处理”。

        

通过以下场景来加深理解:

        

场景情况:假设 test.txt 是一个空文件,执行错误代码:

                

FILE* pf = fopen("test.txt", "r");
while (!feof(pf)) // 循环条件:“没到末尾”就进入
{     int ch = fgetc(pf);  // 读取字符printf("处理的字符:%d\n", ch);  // 处理(打印ASCII值)
}
fclose(pf);
pf = NULL;

   

执行步骤逐行分析:

 
  1. 初始状态:文件为空,feof(pf) 是 (因为还没执行过读取操作,EOF 标志位未设置);
  2. 第一次判断循环条件 !feof(pf) → ,进入循环;
  3. 执行 fgetc(pf):因为文件为空,读取失败,返回 EOF(-1),同时系统设置 feof(pf) 为 (因为读取了超出文件末尾的数据);
  4. 执行 “处理”:打印 处理的字符:-1(这是 无效数据,因为 EOF 不是文件中的有效字符);
  5. 第二次判断循环条件 !feof(pf) → feof 已为 !feof 是 ,退出循环。
 

问题:文件明明为空,却多处理了一次 EOF(-1),这是无效且错误的。

     

        

6.实战演练:拷贝文件

        

  代码示例1:通过写一个程序将源文件拷贝到目标文件(以文本文件的形式)

#include<stdio.h>int main()
{FILE* pf1 = fopen("source.txt", "r"); //以读的形式打开源文件if (pf1 == NULL){perror("fopen:source.txt");return 1;}FILE* pf2 = fopen("destination.txt", "w");  //以写的方式打开目标文件if (pf2 == NULL){//关闭源文件fclose(pf1);perror("fopen:source.txt");return 1;}//将源文件的内容拷贝到目标文件当中char arr[1000];while (fgets(arr, 20, pf1) != NULL){fputs(arr, pf2);}/*int ch = 0;while ((ch = fgetc(pf1)) != EOF){fputc(ch, pf2);}*/if (feof(pf1)){printf("文件读到末尾结束");}else if (ferror(pf1)){printf("文件读取失败而结束");}//关闭文件fclose(pf1);pf1 = NULL;fclose(pf2);pf2 = NULL;return 0;
}

        

代码示例2:通过写一个程序将源文件拷贝到目标文件(以二进制文件的形式)

#include <stdio.h>
#include <stdlib.h>#define BUFFER_SIZE 4096  // 缓冲区大小,建议与系统页大小匹配(如4096字节)int main() 
{// 1. 打开源文件(二进制只读模式)FILE* src_file = fopen("source.bin", "rb");if (src_file == NULL){perror("无法打开源文件");  return 1;}// 2. 打开目标文件(二进制只写模式,不存在则创建)FILE* dest_file = fopen("dest.bin", "wb");if (dest_file == NULL){perror("无法创建目标文件");fclose(src_file);  // 关闭已打开的源文件,避免资源泄漏return 1;}// 3. 循环读写数据(按字节块处理)unsigned char buffer[BUFFER_SIZE];  // 缓冲区(用unsigned char更适合二进制数据)size_t read_bytes;                  // 记录每次实际读取的字节数// 从源文件读取数据到缓冲区,返回实际读取的字节数(0表示文件结束)while ((read_bytes = fread(buffer, 1, BUFFER_SIZE, src_file)) > 0) {// 将缓冲区的数据写入目标文件,确保写入字节数与读取一致size_t written_bytes = fwrite(buffer, 1, read_bytes, dest_file);}// 4. 检查读取过程是否出错(非文件结束导致的失败)if (ferror(src_file)) {perror("读取源文件失败");fclose(src_file);fclose(dest_file);return 1;}// 5. 关闭文件(必须执行,确保数据刷新到磁盘)fclose(src_file);fclose(dest_file);printf("二进制文件拷贝成功!\n");return 0;
}

        

四、文件的随机读写

1.fseek函数

①fseek函数的简介:

        

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

        

核心功能:将流相关位置指针设置为新的位置,重新定位流位置指示器。

        

参数一 FILE * stream:指向标识流的 FILE 对象的指针。

        

参数二 long int offset:从起始位置偏移量

        

参数三 int origin:用作偏移参考的位置

        

origin有如下三个参数:

ConstantReference position  参考位置
SEEK_SET

Beginning of file 

文件开头

SEEK_CURCurrent position of the file pointer
文件指针的当前位置
SEEK_END

End of file   

文件末尾 

        

        

代码示例:从文件开头移动文件指示器,改变文本内容。

        

#include<stdio.h>
int main()
{FILE* pf = fopen("test.txt", "w");//"w"读写文件if (pf == NULL){perror("fopen");return 1;}fputs("hello world", pf);fseek(pf, 6, SEEK_SET);//文件指示器从文件开头向后移动7位fputs("bit!!", pf);//从文件指示器当前位置开始将后面的五个字符替换为bit!!fclose(pf);pf = NULL;return 0;
}

        

代码示例二:从文件指示器当前位置移动

        

#include<stdio.h>
int main()
{FILE* pf = fopen("test.txt", "w");//"w"读写文件if (pf == NULL){perror("fopen");return 1;}fputs("hello world", pf);fseek(pf, 3, SEEK_CUR);//文件指示器从文件开头向后移动3位fputs("hello bit!!", pf);//从文件指示器当前位置开始添加字符串"hello bit!!!"fclose(pf);pf = NULL;return 0;
}

        

代码示例三:通过从文件末尾位置,将文件指示器位置移动到文件开始

        

#include<stdio.h>
int main()
{FILE* pf = fopen("test.txt", "w");//"w"读写文件if (pf == NULL){perror("fopen");return 1;}fputs("hello world", pf);fseek(pf, -11, SEEK_END);//文件指示器从文件末尾向前移动11位//从文件指示器移动到文件开始,将字符串"hello world"替换字符串为"hello bit!!!"fputs("hello bit!!", pf);fclose(pf);pf = NULL;return 0;
}

        

2.ftell函数

②ftell函数的简介:

        

        long int ftell ( FILE * stream );

        

核心功能:获取流中的当前位置与文件开始位置的偏移量

        

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

        

代码示例:获取当前位置与文件起始位置的偏移量

        

#include<stdio.h>
int main()
{FILE* pf = fopen("test.txt", "w");//"w"读写文件if (pf == NULL){perror("fopen");return 1;}fputs("hello world", pf);int gap=ftell(pf);printf("%d", gap);fclose(pf);pf = NULL;return 0;
}

        

3.rewind函数

①rewind函数的简介:

        

        void rewind ( FILE * stream );

        

函数功能:将文件指示器位置设置到文件开头。(不忘初心,重走来时路)

        

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

        

代码示例:将文件指示器位置设置到文件开头

#include<stdio.h>
int main()
{FILE* pf = fopen("test.txt", "w");//"w"读写文件if (pf == NULL){perror("fopen");return 1;}fputs("hello world", pf);int gap = ftell(pf);printf("当前光标位置距离文件起始的距离:%d\n", gap);rewind(pf);gap=ftell(pf);printf("通过rewind函数设置后,光标位置距离文件起始的距离:%d", gap);fclose(pf);pf = NULL;return 0;
}

        

五、文件缓冲区

        文件缓冲区的示意图:

        

        

 1.为什么需要文件缓冲区?

物理设备(如硬盘)的 IO 操作速度远低于内存访问速度。如果程序每次读写一个字节都直接操作磁盘,会导致大量低效的磁盘访问(比如写 1000 个字节就要访问磁盘 1000 次)。

        

缓冲区的解决方案是:

 

        ①写操作:先将数据暂存到内存缓冲区,等缓冲区满了或满足特定条件时,再一次性写入磁盘(1 次 IO 操作代替多次)。

        

        ②读操作:先从磁盘读取一大块数据到缓冲区,程序后续读取时直接从缓冲区获取(减少磁盘访问次数)。

        

        

2.文件缓冲的类型

C 语言中,FILE 结构体(文件指针指向的结构)内部维护了缓冲区,根据刷新策略分为 :

 
  1. 全缓冲(Full Buffering)

    • 适用场景:普通磁盘文件(如 .txt.bin 等)。
    • 刷新时机:当缓冲区被填满时,自动刷新(将数据写入磁盘);或调用 fflush 手动刷新;或关闭文件时自动刷新。
  2. 行缓冲(Line Buffering)

    • 适用场景:与终端相关的流(如 stdin 标准输入、stdout 标准输出)。
    • 刷新时机:当遇到换行符 \n 时自动刷新;或缓冲区满时;或手动调用 fflush;或关闭文件时。
    • 例:printf("hello\n") 会在输出 \n 时,将缓冲区的 "hello\n" 刷新到屏幕。

        

3.代码示例

        

代码示例1:演示行缓冲

#include <stdio.h>
#include <unistd.h>  // 包含sleep函数int main() 
{// stdout是行缓冲,以下代码不会立即输出(因为没有\n)printf("Hello");sleep(3);  // 暂停3秒,期间屏幕无输出// 3秒后,添加\n触发行缓冲刷新,此时才显示"HelloWorld"printf("World\n");sleep(3);return 0;
}

        

代码示例二:演示全缓冲

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

既然看到这里了,不妨点赞+收藏,感谢大家,若有问题请指正。

        

http://www.xdnf.cn/news/1478359.html

相关文章:

  • 数据库集群分类详解
  • 【Java】抽象类和接口对比+详解
  • Altium Designer(AD24)加载License文件方法
  • 计算机CPU的工作原理介绍
  • 抽成独立组件库:微前端架构下公共组件共享的最佳实践
  • MyBatis Example模式SQL注入风险
  • C#中一段程序类比博图
  • 【完整源码+数据集+部署教程】水培植物病害检测系统源码和数据集:改进yolo11-AKConv
  • 从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
  • 【科研成果速递-IJGIS】如何描述与分类移动对象的时空模式?一个新的分类框架与体系!
  • JDBC操作数据库所需要的组件
  • 《Kubernetes 构建 MySQL MGR 集群实战教程》
  • 使用Spring Boot DevTools快速重启功能
  • Python爬虫实战:研究Event Handling机制,构建在线教育平台的课程数据采集和分析系统
  • 使用 YAML 自动化 Azure DevOps 管道
  • browser-use 的三种启动方式详解
  • Android Framework智能座舱面试题
  • 【Python自动化】 21.2 Pandas 读取 Excel 时的 dtype 参数完全指南
  • 贪心算法应用:DNA自组装问题详解
  • Flask论坛与个人中心页面开发教程完整详细版
  • 【LeetCode 热题 100】49. 字母异位词分组
  • Windows 11 手动下载安装配置 uv、配置国内源
  • 固定资产管理系统(vue+Springboot+mybatis)
  • 行为式验证码技术解析:滑块拼图、语序选词与智能无感知
  • Vllm-0.10.1:vllm bench serve参数说明
  • 【完整源码+数据集+部署教程】农作物病害检测系统源码和数据集:改进yolo11-HSFPN
  • Flutter常用库集锦
  • Webpack热更新(HMR)底层原理详解
  • 基于定制开发开源AI智能名片S2B2C商城小程序的DMP平台离线文件上传功能优化研究
  • RK3568 Trust