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

C语言:详解文件操作

C语言:详解文件操作

1.文件相关知识
2.文件的打开和关闭
3.文件的顺序读写
4.文件的随机读写
5.文件读取结束判定
6.文件缓冲区

1.文件相关知识

什么是文件?

磁盘(硬盘)上的文件是文件。

在程序设计中,我们一般谈的文件有两种:程序文件、数据文件。

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

本期讨论的是数据文件。

一个文件要有一个唯一的文件标识,文件名包含3部分:文件路径 + 文件名主干 + 文件后缀,例如:c:\code\test.txt 。最后一个 \ 和 . 之间的是文件主干名,最后一个 \ 之前的内容(包括最后一个 \ )为文件路径,剩下的内容和点号 . 为文件后缀。
在这里插入图片描述
根据数据的组织形式,数据文件被称为文本文件或者二进制文件

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

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

流和标准流

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

C程序针对文件、画面、键盘等的数据输入输出操作都是通过流操作的。一般情况下,我们要想向流里写数据,或者从流中读取数据,都是要打开流,然后操作。

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

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

stdin、stdout、stderr 三个流的类型是: FILE* ,通常称为文件指针。C语言中,就是通过 FILE* 的文件指针来维护流的各种操作。

文件指针

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

下面创建一个FILE*类型的指针变量pf。

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

pf是一个指向FILE类型数据的指针变量,可以使pf指向某个文件的文件信息区,通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够间接找到与它关联的文件。
在这里插入图片描述

2.文件的打开和关闭

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

fopen函数用来打开文件,函数原型如下:

FILE* fopen(const char* filename,const char* mode);
  • 参数filename为文件名,用字符串表示。
  • 参数mode为文件打开模式,用字符串表示。

读和写的意义如图所示:
在这里插入图片描述
文件打开模式如下:
在这里插入图片描述
注意:对于"w",若打开的文件存在且已有信息,则清空原有信息。

fclose函数用来关闭文件,函数原型如下:

int fclose(FILE* stream);

示例:打开一个文件,然后关闭文件。

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","w");if(pf==NULL)  //检查{perror("fopen");  //如果pf为NULL,就会打印 fopen:错误信息 ,perror的参数是一个字符串return 1;}fclose(pf);  //关闭文件pf=NULL;  //把pf置为NULL,避免成为野指针。return 0;
}

程序运行后就会自动创建test.txt文件,按如下方法找到test.txt文件。
在这里插入图片描述
接下来点击 “x” 退出,按下图操作。

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

3.文件的顺序读写

顺序读写函数介绍

在这里插入图片描述
上面说的适用于所有输入流一般指适用于标准输入流和其他输入流(如文件输入流);所有输出流一般指适用于标准输出流和其他输出流(如文件输出流)。

fputc函数

fputc函数可将字符输出到文件中,函数原型如下:

int fputc(int ch,FILE* stream);
  • 参数ch传字符或ASCII码值

示例:在test.txt文件上写字母a~z。

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","w");if(pf==NULL)  //检查{perror("fopen");return 1;}char ch=0;for(ch='a';ch<='z';ch++)  //写入字母a~zfputc(ch,pf);fclose(pf);pf=NULL;return 0;
}

程序运行结束后,我们用记事本打开文件,就能看到文件被写入字母a~z。
在这里插入图片描述
fputc函数的第二个参数是文件指针类型的stream,因为fputc适用于所有输出流,所以我们将上述代码中fputc的参数pf改为stdout,即可把字母a~z输出到屏幕。

#include<stdio.h>
int main()
{char ch=0;for(ch='a';ch<='z';ch++)fputc(ch,stdout)return 0;
}

在这里插入图片描述

fgetc函数

fgetc函数可以从文件中读取字符,函数原型如下:

int fgetc(FILE* stream);
  • 若成功读取字符,返回字符的ASCII码值。
  • 若读取遇到文件末尾,或读取失败,返回EOF(即为 -1)。

示例:把test.txt中的字母a~z显示到屏幕上。

#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;
}   

在这里插入图片描述
fgetc也可以把参数pf改为stdin,这样就能从键盘输入字符,类似scanf。

#include<stdio.h>
int main()
{int ch=0;ch=fgetc(stdin);fputc(ch, stdout);   return 0;
}

在这里插入图片描述

fputs函数

fputs函数可以向文件输出一个字符串,函数原型如下:

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","w");if(pf==NULL){perror("fopen");return 1;}fputs("haha\n",pf);fputs("hehe\n",pf);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述
可以看到,test.txt文件被写入了haha和hehe,由于\n的作用,所以有3行。

同样地,我们把参数pf改为stdout,就能把字符串输出到屏幕。

#include<stdio.h>
int main()
{char str[]="abcdefg";fputs(str,stdout);return 0;
}

在这里插入图片描述

fgets函数

fgets函数能从文件中最多读取一行字符,但不会换行读,函数原型如下:

char* fgets(char* str,int num,FILE* stream);
  • 参数num表示最多读num个字符。
  • 读取成功返回字符串地址,读取失败或遇到文件末尾,返回NULL。

示例:将test.txt中的haha、hehe打印到屏幕上。

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","r");if(pf==NULL){perror("fopen");return 1;}char str[100];while(fgets(str,30,pf)!=NULL)printf("%s",str);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述
同样的,我们把参数pf改为stdin,就能在键盘上输入。

#include<stdio.h>
int main()
{char str[100];fgets(str,5,stdin);printf("%s\n",str);return 0;
}

因为最多只读5个字符,且包含 ‘\0’ ,所以打印结果如下:
在这里插入图片描述

fprintf函数

fprintf函数是格式化输出函数,函数原型如下:

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

示例:把结构体的数据写到文件中。

#include<stdio.h>
struct S
{char name[20];int age;float score;
};
int main()
{struct S s={"张三",20,65.5f};FILE* pf=fopen("test.txt","w");if(pf==NULL){perror("fopen");return 1;}fprintf(pf,"%s %d %f",s.name,s.age,s.score);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述
可以看到,fprintf函数与printf类似,只是fprintf多了一个参数,我们同样可以通过对文件类型参数的调整来使数据输出到屏幕。

#include<stdio.h>
int main()
{int arr[10]={0};for(int i=0;i<10;i++){fprintf(stdout,"%d ",arr[i]);}return 0;
}

在这里插入图片描述

fscanf函数

fscanf函数为格式化输入函数,函数原型如下:

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

示例:把test.txt文件中的数据读入结构体变量s中。

#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;}fscanf(pf,"%s %d %f",s.name,&(s.age),&(s.score));printf("%s %d %f\n",s.name,s.age,s.score);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述
fscanf也与scanf类似,不同之处就在于fscanf函数参数多了一个文件类型指针,我们通过这个指针也可以实现通过键盘输入数据。

#include<stdio.h>
int main()
{int n=0;fscanf(stdin,"%d",&n);fprintf(stdout,"%d",n);return 0;
}

在这里插入图片描述

fwrite函数

fwrite函数为二进制输出函数,返回成功读取的元素个数,只适用于文件输出流,函数原型如下:

size_t fwrite(const void* p,size_t size,size_t count,FILE* stream);
  • 例如p指向数组,每个元素占size个字节,共count个元素。

示例:把arr的数据写到文件test.txt中。

#include<stdio.h>
int main()
{int arr[]={1,2,3,4,5};int len=sizeof(arr)/sizeof(arr[0]);FILE* pf=fopen("test.txt","wb");if(pf=NULL){perror("fopen");return 1;}fwrite(arr,sizeof(int),len,pf);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述
由于文件的打开模式是"wb",所以test.txt文件里的内容是无法读懂的。

fread函数

fread函数是二进制输入函数,读到多少个元素就返回数字多少,只适用于文件输入流,函数原型如下:

size_t fread(void* p,size _t size,size_t count,FILE* stream);
  • size为元素所占字节数,count为元素个数。

示例:把test.txt文件中的二进制数据读到arr。

#include<stdio.h>
int main()
{int arr[5]={0};FILE* pf=fopen("test.txt","rb");if(pf==NULL){perror("fopen");return 0;}int i=0;fread(arr, sizeof(int), 5, pf);for (int i = 0;i < 5;i++){printf("%d ",arr[i]);}fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述

4.文件的随机读写

fseek函数

fseek可根据文件中的光标的位置来读写文件。函数原型如下:

int fseek(FILE* stream,long int,offset,int origin);
  • 参数offset为目标位置到起始位置偏移量。
  • 参数origin为起始位置,文件起始位置为SEEK_SET,光标当前位置为SEEK_CUR,文件末尾为SEEK_END。

示例:

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","w");  //先用"w"把之前文件中的内容清空if(pf==NULL){perror("fopen");return 1;}char str[]="abcdefghij";  //往文件中写入字母a~jfputs(str,pf);fclose(pf);  //关闭文件,刷新缓冲区接下来才能正常操作pf=fopen("test.txt","r");  //再次以"r"打开文件fseek(pf,4,SEEK_SET);  //光标从文件起始位置(向右)偏移4,向左偏移则为负数int ch=fgetc(pf);printf("%c\n",ch);fclose(pf);pf=NULL;return 0;
}

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

ftell函数

ftell函数能返回光标相对起始位置的偏移量,函数原型如下:

long int ftell(FILE* stream);

示例:

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","r");if(pf==NULL){perror("fopen");return 1;}for (int i=0;i<5;i++)  //读取5个字符{fgetc(pf);}int ret=ftell(pf);printf("%d\n",ret);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述

rewind函数

rewind函数能让文件指针的位置回到文件的起始位置,函数原型如下:

void rewind(FILE* stream);

示例:

#include<stdio.h>
int main()
{FILE* pf=fopen("test.txt","r");if(pf==NULL){perror("fopen");return 1;}for (int i=0;i<5;i++)  //移动光标{fgetc(pf);}int ret=ftell(pf);printf("此时光标与起始位置的距离为%d\n",ret);rewind(pf);ret=ftell(pf);printf("此时光标与起始位置的距离为%d\n",ret);fclose(pf);pf=NULL;return 0;
}

在这里插入图片描述

5.文件读取结束判定

文件读取结束的原因可能是 遇到文件末尾 或 读取出错。

文本文件读取是否结束:

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

⼆进制文件的读取是否结束:

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

其他判断方法:

  • feof函数,返回值非0,则遇到文件末尾。
int feof(FILE* stream);
  • ferror函数,错误标记被设置返回非0,否则返回0。
int ferror(FILE* stream);

6.文件缓冲区

ANSIC 标准采用 “缓冲文件系统” 处理的数据文件,缓冲文件系统是指:系统自动地在内存中为程序中每⼀个正在使用的文件开辟一块 “文件缓冲区”。

若从内存向磁盘(硬盘)输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。

如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。

缓冲区的大小根据C编译系统决定的。
在这里插入图片描述
因为有缓冲区的存在,C语言在操作文件的时候,需要做 刷新缓冲区 或者 在文件操作结束的时候关闭文件(用fclose关闭文件)。如果不做,可能导致读写文件的问题。

拙作一篇,望诸位同道不吝斧正。

http://www.dtcms.com/a/296312.html

相关文章:

  • 【Java工程师面试全攻略】Day12:系统安全与高可用设计
  • 嵌入式linux I2C 设备开发调试 使用i2cget 工具失败的问题
  • JavaScript性能优化实战指南:从原理到最佳实践
  • Flink-1.19.0源码详解7-Flink集群端调度
  • 跨境支付入门~国际支付结算(区块链篇)
  • jina-embedding-v4 环境搭建全过程
  • 2025年“创新杯”(原钉钉杯) B题 详细建模思路
  • 牛客网刷题进阶挑战VL25——VL49
  • 【Linux网络编程】传输层协议 - UDP
  • 【数据结构初阶】--二叉树(二)
  • M²IV:面向大型视觉-语言模型中高效且细粒度的多模态上下文学习
  • BI 系统数据看板全解析:让数据可视化驱动业务决策
  • ESP32使用 vscode IDF 创建项目到烧录运行全过程
  • C++学习笔记(八:函数与变量)
  • 云原生架构下的服务器运维挑战与解决方案
  • 【CVPR 2025】即插即用,MobileMamba三阶段架构+Wavelet增强,颠覆轻量模型格局!
  • Qt Quick 3D渲染
  • 云端哨兵的智慧觉醒:Deepoc具身智能如何重塑工业无人机的“火眼金睛”
  • 5种最佳方法将iPhone语音备忘录传输到Mac
  • 清除浮动以及原理
  • 移动管家手机控车便捷性如何
  • 秋招Day18 - MyBatis - 基础
  • tensorflow安装(CPU版本)
  • 爬虫算法原理解析
  • Python爬虫实战:研究picloud相关技术
  • WebRTC指纹——深度分析(中篇)
  • qlib的Alpha158类定义
  • RHCE(4)
  • CDH yarn 重启后RM两个备
  • 2025.7.24 01背包与动态规划复习总结