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

C语言进阶篇(文件操作)

一.文件的种类(从文件功能的角度来分类的)

1.1程序文件

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

1.2数据文件

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

1.3文件名

文件名包括三部分:文件路径+文件名主干+文件后缀

例如:c:\code\test.txt

二.文件的打开和关闭

2.1文件指针

每个被使用的文件都会在内存中开辟一个相应的文件信息区,用来存放文件的相关信息,这些信息保存在一个结构体变量中 ,该结构体类型有系统声明,取名为FILE,也就是说FILE是一个结构体 。

此为VS2013提供的stdio.h头文件中有的文件类型申明

当我们使用fopen打开文件时,会在内存里创建文件信息区,并且会将文件信息区的起始地址返回回来

一般都是通过FILE的指针来维护FILE结构中的变量,即:

FILE* pf //文件指针变量

可通过pf指向某个文件的文件信息区,再通过文件信息区访问文件,即:通过文件指针变量能够找到与它关联的文件。

2.2文件的打开和关闭

在打开文件时,都会返回一个FILE*的指针变量指向该文件

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

fopen:

格式:FILE* fopen(const char* filename, const char* mode); //FILE*是fopen的返回类型

  • 第一个参数是文件名(相对路径)。
  • 第二个参数是打开模式,常用的有:
模式含义核心特性
r只读打开已存在的文件,文件不存在则打开失败
w只写文件不存在则创建,文件已存在则清空内容(覆盖)//在fopen打开文件时清空
a追加文件不存在则创建,写入内容追加到文件末尾
r+读写打开已存在的文件,可读可写,不清空内容
w+读写文件不存在则创建,已存在则清空内容,可读可写
a+读写文件不存在则创建,写入追加到末尾,可读

如果在后面加上b,即rb、wb等,就表示是二进制模式

fclose:

格式:int fclose ( FILE * stream );

使用示例:

#include<stdio.h> #include<string.h> #include<errno.h> int main() { //打开文件 FILE*pf=fopen("text.c","r");//若打开失败,则pf会返回空指针 if(pf==NULL) { printf("%s\n",strerror(errno)); return 1; } //关闭文件 fclose(pf); pf=NULL; return 0; }

路径分为相对路径和绝对路径:

  • 相对路径:以当前所在目录为起点,简写路径,依赖当前位置。 例:"text.c"
  • 绝对路径:从根目录开始,完整、唯一的路径,不受当前所在位置影响。 例:"C:\Users\hpl13\Desktop\text.c"

注:当在代码中使用fopen打开绝对路径时应用\\ 例: FILE*pf=fopen("C:\\Users\\hpl13\\Desktop\\text.c","r");

三.文件的顺序读写

3.1关于文件读写的函数

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

输入=读,输出=写

  • 键盘输入、文件读取、网络数据接收,本质上都是数据从外部流向程序,这叫输入流
  • 屏幕打印、文件写入、网络数据发送,本质上都是数据从程序流向外部,这叫输出流

fgetc(读一个字符)

格式:int fgetc ( FILE * stream );

使用示例:

#include<stdio.h> #include<string.h> #include<errno.h> int main() { //打开文件 FILE*pf=fopen("text.c","r"); if(pf==NULL) { printf("%s\n",strerror(errno)); } //读文件 int ch=fgetc(pf); printf("%c\n"ch);//一次只读一个字符 //以下方法可将文件内的内容全部读出来: int ch=0; while((ch=fgetc(pf))!=EOF) { printf("%c ",ch); } //关闭文件 fclose(pf); pf=NULL; return 0; }

fputc(写一个字符)

格式:int fputc ( int character, FILE * stream );

  • 第一个参数是写入的字符。
  • 第二个参数是文件指针变量名。

使用示例:

#include<stdio.h> #include<string.h> #include<errno.h> int main() { //打开文件 FILE*pf=fopen("text.c","w"); if(pf==NULL) { printf("%s\n",strerror(errno)); } //写文件 int i=0; for(i='a';i<='z';i++); { fputc(i,pf); } //关闭文件 fclose(pf); pf=NULL; return 0; }

fgets(读一行字符)

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

  • 第一个参数是指向一个字符数组的指针,读取的字符串将被复制到该数组中。
  • 第二个参数是读取的字节数(包括\0,且\0算一个字符)
  • 第三个参数文件指针变量名。

使用示例:

#include<stdio.h> #include<string.h> #include<errno.h> int main() { //打开文件 FILE*pf=fopen("text.c","r"); if(pf==NULL) { printf("%s\n",strerror(errno)); } //读一行字符 char arr[20]; fgets(arr,5,pf); printf("%s",arr) //关闭文件 fclose(pf); pf=NULL; return 0; }

fputs(写一行字符)

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

使用示例:

#include<stdio.h> #include<string.h> #include<errno.h> int main() { //打开文件 FILE*pf=fopen("text.c","w"); if(pf==NULL) { printf("%s\n",strerror(errno)); } //写一行字符 fputs("hello world",pf); //关闭文件 fclose(pf); pf=NULL; return 0; }

补充:perror

头文件:#include<stdio.h>

当我们判断pf是否为空指针NULL时,可用perror来代替printf("%s",strerror(errno));

例:

FILE*pf=fopen("text.c","w"); if(pf==NULL) { perror(fopen:); }

结果:

fprintf(格式化写入)

格式:int fprintf ( FILE * stream, const char * format, ... );//与printf相似,只比printf多了第一个参数

使用示例:

struct S { char name[10]; int age; float weight; }; int main() { struct S s={"zhangsan",13,50.2}; FILE*pf=fopen("text.c","w"); if(pf==NULL) { perror(fopen); } fprintf(pf,"%s %d %f",s.name,s.age,s.weight); fclose(pf); pf=NULL; return 0; }

fscanf(格式化读)

格式:int fscanf ( FILE * stream, const char * format, ... );//与scanf相似,只比scanf多了第一个参数

使用示例:

struct S { char name[10]; int age; float weight; }; int main() { struct S s={0}; FILE*pf=fopen("text.c","r"); if(pf==NULL) { perror(fopen); } fscanf(pf,"%s %d %f",s.name,&(s.age),&(s.weight)); printf("%s %d %f",s.name,s.age,s.weight); fclose(pf); pf=NULL; return 0; }

补充:流

任何一个C语言程序,只要运行起来就默认打开三个流:

FILE* stdin - 标准输入流(键盘)

FILE* stdout - 标准输出流(屏幕)

FILE* stderror - 标准错误流(屏幕)

如果将fprintf的第一个参数由pf换为stdout,那么就会写入到屏幕上,对于fputs和fputc也一样

fwrite(二进制方法写)

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

  • 第一个参数是指向你要写入文件的数据的起始地址(数据从哪里来)
  • 第二个参数是一个元素的大小
  • 第三个参数是有几个这样的元素

使用实例:

  1. FILE*pf={"test.c","wd"}; int arr[] = {10,20,30}; fwrite(arr, sizeof(int), 3, pf);
  2. FILE*pf={"test.c","wd"}; struct S s={"zhangsan",18,65.2f}; fwrite(&s,sizeof(struct S),1,pf);

通过二进制的方式写进去,这会让我们肉眼看不懂,这时就应该用fread以二进制的方式来读

fread(二进制方法读)

格式:size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );//与fwrite相同

使用示例:

FILE*pf={"test.c","rd"}; struct S s={"zhangsan",18,65.2}; fread(&s,sizeof(struct S),1,pf); printf("%s %d %f",s.arr,s.age,s.weight);

3.2关于一些函数的差异

scanf 是针对标准输入的格式化输入语句(键盘)

printf 是针对标准输出的格式化输出语句(键盘)

fscanf 是针对所有输入流的格式化输入语句(文件、键盘输入都可以)

fprintf 是针对所有输出流的格式化输出语句(文件、键盘输入都可以)

sscanf

作用:从一个字符串中转化出一个格式化的数据

#include<stdio.h> struct S { char name[20]; int age; float weight; }; int main() { struct S s={"zhangsan",18,65.2f}; struct S tmp = { 0 }; char buf[100] = { 0 }; //把s中的格式化数据转化成字符串放到buf中 sprintf(buf, "%s %d %f", s.arr, s.age, s.score); //"zhangsan 20 55.500000"; printf("字符串: %s\n", buf); //从字符串buf中获取一个格式化的数据到tmp中 sscanf(buf, "%s %d %f", tmp.arr, &(tmp.age), &(tmp.score)); printf("格式化: %s %d %f\n", tmp.arr, tmp.age, tmp.score); return 0 }

sprintf

作用:把一个格式化的数据写到字符串中,本质是把一个格式化的数据转换成字符串。

格式:int sprintf ( char * str, const char * format, ... );//与printf相似,只比其多了第一个参数

使用示例:

#include<stdio.h> struct S { char name[20]; int age; float weight; }; int main() { struct S s={"zhangsan",18,65.2f}; char buf[100]={0}; sprintf(buf,"%s %d %f",s.name,s.age,s.weight); printf("%s\n",buf); return 0; }

四.文件的随机读写

4.1fseek

作用:指定文件流的读写位置,想从文件哪里读 / 写,就把指针移到哪里。

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

  • 第二个参数是偏移量
  • 第三个参数是起始位置
常量参考位置
SEEK_SET文件开头
SEEK_CUR文件指针的当前位置
SEEK_END文件末尾

使用示例:

#include<stdio.h> int main() { FILE*pf=fopen("test.c","r");//test.c文件里存放的内容:abcdef if(pf==NULL) { perror(open:); return 1; } fseek(pf,2,SEEK_SET);//与文件开头位置的偏移量为2(偏移量可以为负数) int ch=fputc(pf); printf("%c",ch);//c fseek(pf,2,SEEK_CUR); ch=fputc(pf); printf("%c",ch);//f return 0; }

疑问:第二fseek为什么偏移量是 2 而不是 3?

解释:关键在于:fgetc读取一个字符后,文件指针会自动向后移动 1 位

4.2ftell

作用:返回文件指针当前的读写位置(距离文件开头的字节偏移量)。

格式:long int ftell ( FILE * stream );

使用示例:

fseek(pf,2,SEEK_SET);//与文件开头位置的偏移量为2(偏移量可以为负数) int ch=fputc(pf); printf("%c\n",ch);//c printf("%d\n",ftell(pf));//3 fseek(pf,2,SEEK_CUR); ch=fputc(pf); printf("%c",ch);//f printf("%d\n",ftell(pf));//6

4.3rewind

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

格式:void rewind ( FILE * stream );

使用示例:

fseek(pf,2,SEEK_SET); int ch=fputc(pf); printf("%c\n",ch);//c printf("%d\n",ftell(pf));//3 rewind(pf); ch=fputc(pf); printf("%c",ch);//a printf("%d\n",ftell(pf));//1

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

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

二进制文件:数据在内存中以二进制的形式存储,不加转换的输出到外存(我们看不懂)

文本文件:在外存上以ASCLL字符的形式存储的文件(我们看得懂)

数据在内存中的存储方式:

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

以10000为例:

把10000转换为16进制,也就是10000 = 0x00002710

当为大段存储时:00 00 27 10

当为小段存储时:10 27 00 00

六.文件读取结束的判定

6.1错误地使用feof

6.1.1feof

作用:检测上一次读操作是否遇到了文件末尾千万不能用来预判文件有没有结束

应用于:判断是否读取失败结束,还是遇到文件尾结束

格式:int feof ( FILE * stream );

返回值:文件结束 → 返回非 0 值没结束 → 返回 0

feof(pf)!=0:上一次读操作正常碰到文件末尾,正常结束

使用示例:

char buf[100]; while(fgets(buf, 100, fp) != NULL) { printf("%s", buf); } if(feof(fp)) { printf("正常读到末尾\n"); }

6.1.2ferror

格式:int ferror ( FILE * stream );

作用:判断是否因【读取错误】结束

返回值:上一次文件操作发生错误->返回非 0;操作正常,无错误->返回 0

ferror(fp) != 0:上一次读操作发生 IO 错误,异常失败结束

总结:

  • feof:只管是不是撞到文件尾、正常读完
  • ferror:只管是不是读写错误、异常失败

6.1.3用来判断文件是否读取结束的方法

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

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

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

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

count是我们实际要读的个数(我们要fread读的),而fread的返回值等于fread实际读到值的个数

  • 文件读到末尾:已经读完文件里所有有效数据,此时缓冲区还有最后一个有效数据
  • 文件读取结束:在读完所有数据后,又尝试再读一次,读操作失败

七.文件缓冲区

文件缓冲区:是C 语言标准库在内存中开辟的一块临时存储区域,用来暂存程序和磁盘文件之间的数据。缓冲区的大小根据编译系统决定

数据先写到输出缓冲区中,等到输出缓冲区满了后,再一次性写到硬盘中,但是也有库函数可以设置缓冲区的大小

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

http://www.jsqmd.com/news/706369/

相关文章:

  • 基于多模态大模型与智能体协作的像素艺术生成技术实践
  • 设备检测库device-detector:从UA解析到精细化运营的实战指南
  • 2026年人力资源数据分析的技术价值与应用前景
  • 第五章-05-练习案例:升级版自动查核酸
  • 2015-2025年地级市公共安全基建省内横向压力
  • 2026专业户外路灯TOP5推荐:LED路灯、乡村路灯、农村太阳能路灯、太阳能路灯安装、太阳能路灯工厂、太阳能路灯批发选择指南 - 优质品牌商家
  • WebCanvas:可视化AI工作流引擎的设计与实现
  • Windows更改远程桌面3389端口
  • 基于Node.js与Vue 3的轻量级服务器监控仪表盘实战
  • 安装OpenCV-Python 3.4.1.15和opencv-contrib-python 3.4.1.15,并将anaconda prompt创建的python3.6虚拟环境加到pycharm中
  • AI应用开发实战指南:从架构设计到生产部署的完整路径
  • 2026义乌正规诉讼律师机构名录:义乌离婚诉讼咨询、义乌诉讼律师公司、义乌刑事离婚律师、义乌律师公司、义乌离婚律师公司选择指南 - 优质品牌商家
  • 【SSD202 开发实战 18】JPEG 编解码与图片处理
  • 2026年3月优秀的机器人第七轴源头厂家推荐,车铣复合机自动化上下料核心设备/压铸机械手,机器人第七轴源头厂家哪家靠谱 - 品牌推荐师
  • LLM应用开发工具全景指南:从RAG到智能体的高效选型与实践
  • 稀疏矩阵在机器学习中的高效应用与优化技巧
  • 时间序列分析:自相关与偏自相关函数详解
  • AI Agent 面试题 014:Agent的动作空间(Action Space)设计有哪些最佳实践?
  • 2026年Q2燕窝选购技术指南:燕窝哪个牌子最好、燕窝哪个牌子最正宗、燕窝哪种品质好、燕窝如何挑选好的、燕窝排名选择指南 - 优质品牌商家
  • 【2026年版|建议收藏】小白程序员必看!大模型核心概念Agent Skills详解
  • wanwu框架:中文AI应用开发全栈解决方案,从RAG到智能体工作流
  • 2026可靠链板输送带优质供应商推荐榜:链条传动网带、链板提升机、链板输送机、食品输送网带、304不锈钢网带、冲孔链板选择指南 - 优质品牌商家
  • Java——Stream流
  • Devart数据连接工具全解析与26周年庆优惠指南
  • 定义类的方法和CRC建模
  • AI Agent 面试题 015:如何实现Agent的多模态感知能力?
  • SwiftLLM:专为研究设计的轻量级LLM推理引擎
  • python缺陷检测
  • 彻底搞懂:Spring Boot/Cloud 中 bootstrap.yml 与 application.yml 的区别与最佳实践
  • 机器学习分类算法实战:5大核心方法详解