提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 本文主要讲解文件操作相关的知识。
- 一、文件是什么?
- 1.文件的作用与分类
- 2.文件名结构
- 3.二进制文件与文本文件
- 4.数据文件的应用场景
- 二、文件的打开和关闭(C语言)
- 1. 文件打开(fopen函数)
- 基本语法
- 参数说明
- 常用打开模式
- 二进制文件模式
- 返回值
- 示例代码
- 2. 文件关闭(fclose函数)
- 基本语法
- 参数说明
- 返回值
- 示例代码
- 3. 注意事项
- 4. 完整示例
- 5. 常见问题
- 三、文件的顺序读写
- 1. 基本概念
- 2. 顺序读写的函数
- 2.1 字符读写
- 2.2 字符串读写
- 2.3 格式化读写
- 3. 应用示例
- 3.1 文件复制
- 3.2 日志记录
- 4. 注意事项
- 5. 性能考虑
- 四、文件的随机读写
- 1. 文件指针的概念
- 2. 文件指针定位函数
- fseek函数
- ftell函数
- rewind函数
- 3. 随机读写示例
- 示例1:读取文件中间部分内容
- 示例2:修改二进制文件中的特定记录
- 4. 注意事项
- 五、文件缓冲区(C语言)
- 1. 缓冲区的概念与作用
- 2. 缓冲区的类型
- 3. 缓冲区相关函数
- 4. 缓冲区使用注意事项
- 5. 缓冲区示例代码
- 六、更新文件(C语言)
- 1. 文件更新概述
- 2. 基本操作函数
- 2.1 fopen()函数
- 2.2 fseek()函数
- 3. 常见更新操作示例
- 3.1 追加内容
- 3.2 修改指定位置内容
- 3.3 插入内容
- 4. 注意事项
- 5. 高级技巧
- 5.1 内存映射文件
- 5.2 事务处理
- 6. 实际应用场景
- 总结
前言
本文主要讲解文件操作相关的知识。
一、文件是什么?
1.文件的作用与分类
持久化存储数据
程序运行时产生的数据存储在内存中,程序退出后内存被回收,数据丢失。文件将数据保存到磁盘(硬盘),实现数据的持久化存储,确保程序多次运行时可访问历史数据。
文件的分类
从功能角度分为程序文件和数据文件:
- 程序文件:包括源代码(
.c)、目标文件(.obj)、可执行程序(.exe)。 - 数据文件:存储程序运行时读写的数据,如输入数据或输出结果。本章重点讨论数据文件。
2.文件名结构
文件标识由三部分组成,例如c:\code\test.txt:
- 文件路径:
c:\code\ - 文件名主干:
test - 文件后缀:
.txt
3.二进制文件与文本文件
根据数据组织形式分为两类:
- 文本文件:以ASCII码存储字符数据。例如整数
10000以ASCII形式存储占5字节(每个字符1字节)。 - 二进制文件:直接以内存中的二进制形式存储数据。同一整数
10000仅占4字节(按整型存储)。
存储差异示例
- 文本文件:字符按ASCII存储,数值可转为ASCII或二进制。
- 二进制文件:数据无转换,存储紧凑,效率更高。
4.数据文件的应用场景
- 终端交互:默认从键盘输入、输出到显示器。
- 磁盘文件:需长期保存或大量数据时,读写磁盘文件更高效。
二、文件的打开和关闭(C语言)
在C语言中,文件操作是程序与外部存储设备交互的重要方式。要操作文件,首先需要打开文件,操作完成后需要关闭文件。以下是关于文件打开和关闭的详细说明:
1. 文件打开(fopen函数)
基本语法
FILE*fopen(constchar*filename,constchar*mode);参数说明
filename:要打开的文件名(包含路径)mode:打开模式,指定文件的操作方式
常用打开模式
| 模式 | 描述 | 文件存在 | 文件不存在 |
|---|---|---|---|
| “r” | 只读 | 打开成功 | 返回NULL |
| “w” | 只写 | 清空内容 | 创建新文件 |
| “a” | 追加 | 追加内容 | 创建新文件 |
| “r+” | 读写 | 打开成功 | 返回NULL |
| “w+” | 读写 | 清空内容 | 创建新文件 |
| “a+” | 读写 | 追加内容 | 创建新文件 |
二进制文件模式
在以上模式后加"b"表示二进制模式,如"rb"、"wb+"等
返回值
- 成功:返回FILE指针(文件指针)
- 失败:返回NULL
示例代码
FILE*fp;fp=fopen("example.txt","r");if(fp==NULL){printf("文件打开失败\n");exit(1);// 退出程序}2. 文件关闭(fclose函数)
基本语法
intfclose(FILE*stream);参数说明
stream:要关闭的文件指针
返回值
- 成功:返回0
- 失败:返回EOF(-1)
示例代码
if(fclose(fp)!=0){printf("文件关闭失败\n");}3. 注意事项
- 错误处理:每次打开文件后都应检查返回值是否为NULL
- 资源释放:打开的文件必须关闭,否则可能导致资源泄露
- 缓冲区刷新:fclose会刷新缓冲区,确保数据写入文件
- 路径表示:Windows中使用"\“或”/“表示路径,如"C:\data\file.txt”
4. 完整示例
#include<stdio.h>#include<stdlib.h>intmain(){FILE*fp;// 打开文件fp=fopen("data.txt","w");if(fp==NULL){printf("无法创建文件\n");return1;}// 文件操作...fprintf(fp,"这是写入文件的内容\n");// 关闭文件if(fclose(fp)!=0){printf("文件关闭出错\n");return1;}return0;}5. 常见问题
- 权限问题:如果没有写权限,以写模式打开文件会失败
- 文件锁定:某些系统会锁定正在使用的文件
- 路径问题:相对路径是相对于程序运行时的当前目录
- 缓冲区问题:未关闭文件可能导致数据未完全写入
三、文件的顺序读写
1. 基本概念
顺序读写是指按照文件中数据的物理存储顺序依次进行读写操作。与随机读写不同,顺序读写每次操作都会从当前位置开始,完成后文件指针会自动移动到下一个位置。
2. 顺序读写的函数
C语言提供了多个用于顺序读写文件的函数:
2.1 字符读写
fgetc(): 从文件中读取一个字符intch=fgetc(fp);// fp为文件指针fputc(): 向文件写入一个字符fputc('A',fp);// 向文件写入字符'A'
2.2 字符串读写
fgets(): 从文件中读取一行字符串charstr[100];fgets(str,100,fp);// 最多读取99个字符fputs(): 向文件写入字符串fputs("Hello World",fp);
2.3 格式化读写
fscanf(): 从文件格式化读取intage;fscanf(fp,"%d",&age);// 从文件读取整数fprintf(): 向文件格式化写入fprintf(fp,"Name: %s, Age: %d","John",25);
3. 应用示例
3.1 文件复制
#include<stdio.h>intmain(){FILE*src=fopen("source.txt","r");FILE*dest=fopen("dest.txt","w");if(src==NULL||dest==NULL){printf("文件打开失败");return1;}intch;while((ch=fgetc(src))!=EOF){fputc(ch,dest);}fclose(src);fclose(dest);return0;}3.2 日志记录
#include<stdio.h>#include<time.h>voidlog_message(constchar*msg){FILE*log=fopen("app.log","a");if(log==NULL)return;time_tnow;time(&now);fprintf(log,"[%s] %s\n",ctime(&now),msg);fclose(log);}4. 注意事项
- 每次读写操作后,文件指针会自动后移
- 到达文件末尾时,
fgetc()会返回EOF - 打开文件后必须检查文件指针是否为NULL
- 操作完成后必须调用
fclose()关闭文件 - 对于文本文件,不同操作系统可能有不同的换行符表示方式
5. 性能考虑
- 对于大文件,逐个字符读写效率较低
- 可以考虑使用缓冲区或块读写提高性能
- 频繁打开关闭文件会影响性能,应尽量减少操作次数
四、文件的随机读写
1. 文件指针的概念
文件指针是一个指示当前读写位置的标记,它记录了在文件中进行操作的位置。在随机读写文件时,我们需要通过移动文件指针来定位到特定的位置进行操作。
文件指针具有以下特性:
- 打开文件时,指针默认指向文件开头(位置0)
- 每次读写操作后,指针会自动移动到下一个位置
- 可以通过特定函数手动移动指针位置
2. 文件指针定位函数
fseek函数
intfseek(FILE*stream,longoffset,intorigin);- 参数说明:
- stream:文件指针
- offset:偏移量(字节数),可正可负
- origin:基准位置,可取以下值:
- SEEK_SET:文件开头
- SEEK_CUR:当前位置
- SEEK_END:文件末尾
示例:
fseek(fp,10,SEEK_SET);// 将指针移动到距文件开头10字节处fseek(fp,-5,SEEK_END);// 将指针移动到距文件末尾前5字节处ftell函数
longftell(FILE*stream);返回当前文件指针的位置(相对于文件开头的字节偏移量)
示例:
longpos=ftell(fp);// 获取当前指针位置rewind函数
voidrewind(FILE*stream);将文件指针重置到文件开头,相当于:
fseek(fp,0,SEEK_SET);3. 随机读写示例
示例1:读取文件中间部分内容
FILE*fp=fopen("data.txt","r");if(fp==NULL){perror("文件打开失败");return;}// 定位到文件中间位置fseek(fp,0,SEEK_END);longsize=ftell(fp);fseek(fp,size/2,SEEK_SET);// 读取中间部分内容charbuffer[100];fread(buffer,1,50,fp);fclose(fp);示例2:修改二进制文件中的特定记录
typedefstruct{intid;charname[20];floatscore;}Student;// 修改第3条记录的成绩FILE*fp=fopen("students.dat","r+");if(fp==NULL){perror("文件打开失败");return;}// 定位到第3条记录fseek(fp,2*sizeof(Student),SEEK_SET);Student stu;fread(&stu,sizeof(Student),1,fp);// 修改成绩stu.score=95.5;// 写回文件fseek(fp,2*sizeof(Student),SEEK_SET);fwrite(&stu,sizeof(Student),1,fp);fclose(fp);4. 注意事项
二进制文件与文本文件:
- 文本文件的随机读写可能会因系统不同而产生差异
- 二进制文件的随机读写更加可靠
边界检查:
- 移动指针时要注意不要超出文件范围
- 可以使用ftell和fseek结合检查文件大小
错误处理:
- 检查fseek的返回值(成功返回0,失败返回非0)
- 处理可能的文件定位错误
性能考虑:
- 频繁的随机访问可能会降低IO性能
- 对于大量随机访问,考虑使用内存缓存
五、文件缓冲区(C语言)
1. 缓冲区的概念与作用
文件缓冲区是内存中的一块区域,用于临时存储文件读写的数据。缓冲区的主要作用包括:
- 提高I/O效率:减少直接访问磁盘的次数,通过批量读写提高性能
- 协调速度差异:解决CPU处理速度与磁盘I/O速度不匹配的问题
- 数据暂存:为流式I/O操作提供数据中转站
示例场景:当程序调用fwrite()写入100字节数据时,数据不会立即写入磁盘,而是先存入缓冲区,待缓冲区满或显式刷新时才执行实际磁盘写入。
2. 缓冲区的类型
C语言中主要有三种缓冲模式:
全缓冲(Fully Buffered)
- 缓冲区满时才执行实际I/O操作
- 典型应用:普通文件读写
- 缓冲区大小通常为BUFSIZ(在stdio.h中定义,通常为512或4096字节)
行缓冲(Line Buffered)
- 遇到换行符’\n’或缓冲区满时刷新
- 典型应用:终端I/O(stdin/stdout)
- 示例:printf()输出通常会在遇到\n或程序正常结束时自动刷新
无缓冲(Unbuffered)
- 立即执行I/O操作
- 典型应用:stderr错误输出
- 示例:perror()会立即将错误信息输出到屏幕
3. 缓冲区相关函数
设置缓冲模式
intsetvbuf(FILE*stream,char*buf,intmode,size_tsize);- 参数说明:
- stream:文件指针
- buf:用户提供的缓冲区(为NULL时由库自动分配)
- mode:_IOFBF(全缓冲)/_IOLBF(行缓冲)/_IONBF(无缓冲)
- size:缓冲区大小
- 参数说明:
强制刷新缓冲区
intfflush(FILE*stream);- 将缓冲区内容立即写入文件
- stream为NULL时刷新所有输出流
关闭文件时的自动刷新
intfclose(FILE*stream);- 关闭文件前会自动调用fflush()
- 示例:程序异常终止时可能丢失未刷新的缓冲区数据
4. 缓冲区使用注意事项
数据一致性问题
- 重要数据应及时fflush(),防止程序崩溃导致数据丢失
- 示例:数据库日志记录应先fflush()再执行关键操作
性能调优
- 大文件处理时可适当增大缓冲区提高性能
- 示例:视频处理程序可设置8KB或更大的缓冲区
跨平台差异
- 不同系统下默认缓冲区大小可能不同
- 示例:Windows和Linux的BUFSIZ可能不同
线程安全
- 多线程环境下操作同一文件需加锁保护
- 示例:使用flockfile()/funlockfile()函数族
5. 缓冲区示例代码
#include<stdio.h>#include<stdlib.h>intmain(){FILE*fp=fopen("test.dat","w");if(!fp){perror("fopen failed");returnEXIT_FAILURE;}// 设置8KB的全缓冲区charbuf[8192];if(setvbuf(fp,buf,_IOFBF,sizeof(buf))!=0){perror("setvbuf failed");fclose(fp);returnEXIT_FAILURE;}// 写入数据(先存入缓冲区)for(inti=0;i<1000;i++){fprintf(fp,"Record %d\n",i);}// 强制刷新缓冲区fflush(fp);// 修改为行缓冲模式setvbuf(fp,NULL,_IOLBF,0);fprintf(fp,"This will be line buffered\n");fclose(fp);returnEXIT_SUCCESS;}六、更新文件(C语言)
1. 文件更新概述
在C语言中,文件更新操作主要包括以下几种方式:
- 追加内容到文件末尾
- 修改文件中特定位置的内容
- 替换文件中的部分内容
- 插入新内容到文件指定位置
2. 基本操作函数
2.1 fopen()函数
FILE*fopen(constchar*filename,constchar*mode);常用打开模式:
- “r”:只读
- “w”:只写(会清空原文件)
- “a”:追加(在文件末尾写入)
- “r+”:读写(从文件头开始)
- “w+”:读写(清空原文件)
- “a+”:读写(从文件末尾开始)
2.2 fseek()函数
intfseek(FILE*stream,longoffset,intwhence);参数说明:
whence取值:- SEEK_SET:从文件头开始
- SEEK_CUR:从当前位置开始
- SEEK_END:从文件末尾开始
3. 常见更新操作示例
3.1 追加内容
FILE*fp=fopen("example.txt","a");if(fp!=NULL){fprintf(fp,"This will be appended to the end.\n");fclose(fp);}3.2 修改指定位置内容
FILE*fp=fopen("example.dat","r+");if(fp!=NULL){fseek(fp,10,SEEK_SET);// 定位到第10字节fwrite("new data",1,8,fp);// 写入8字节新数据fclose(fp);}3.3 插入内容
// 1. 打开源文件和临时文件FILE*src=fopen("source.txt","r");FILE*tmp=fopen("temp.txt","w");// 2. 复制前半部分到临时文件intpos=0;while(pos<insert_position){intch=fgetc(src);fputc(ch,tmp);pos++;}// 3. 写入要插入的内容fputs("New inserted content\n",tmp);// 4. 复制剩余部分intch;while((ch=fgetc(src))!=EOF){fputc(ch,tmp);}// 5. 关闭文件并替换fclose(src);fclose(tmp);remove("source.txt");rename("temp.txt","source.txt");4. 注意事项
- 错误处理:所有文件操作都应检查返回值
- 缓冲区刷新:必要时使用fflush()确保数据写入
- 文件锁定:在多进程/线程环境中可能需要文件锁定
- 性能考虑:频繁的小规模更新可能影响性能
5. 高级技巧
5.1 内存映射文件
对于大文件更新,可以使用内存映射提高效率:
#include<sys/mman.h>// 使用mmap()函数将文件映射到内存5.2 事务处理
实现简单的文件更新事务:
- 创建临时文件
- 执行所有更新操作
- 确认无误后替换原文件
- 出现错误则回滚
6. 实际应用场景
- 日志文件更新:追加新日志记录
- 配置文件修改:更新特定配置项
- 数据库操作:修改数据记录
- 游戏存档:更新玩家进度
总结
文件操作对于信息的储存和再利用非常重要。