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

高阶C语言之五:(数据)文件

目录

文件名

 文件类型

文件指针

 文件的打开和关闭

文件打开模式 

文件操作函数(顺序)

0、“流”

1、字符输出函数fputc

2、字符输入函数fgetc 

3、字符串输出函数fputs

4、 字符串输入函数fgets

 5、格式化输入函数fscanf

6、格式化输出函数fprintf

7、二进制输入函数fread

8、二进制输出函数fwrite

 几种输入输出

文件随机读写函数

1、fseek:定位指针

2、ftell:返回指针位置

3、rewind:重定位指针

4、实例

 文本文件和二进制文件

 文件读取结束的判定

1、被错误使用的feof函数

2、判断文件是否读取结束:

3、实例

文件缓冲区


改造通讯录(可保存信息):month_11/test_12 · Hera_Yc/bit_C_学习 - 码云 - 开源中国

本文相关代码:month_11/test_16/main.c · Hera_Yc/bit_C_学习 - 码云 - 开源中国

文件:即硬盘上的文件,文件将数据直接存放在电脑的硬盘上,做到了数据的持久化

在程序设计当中,程序员所说的文件一般分为两种:程序文件、数据文件。

  • 程序文件:包括源程序文件(后缀为 .c )、目标文件(windows环境后缀为 .obj )、可执行程序文件(windows环境后缀为 .exe )。
  • 数据文件:文件的内容不一定是程序,而是程序运行时读写的数据。

个人对文件操作的理解:

  1. 文件操作这里的本质就是一堆库函数,懂得这些库函数就可以对函数进行操作了。
  2. 文件的使用与动态内存类似,需要“开辟”和“释放”,即文件的打开和关闭。
  3. 相对于内存来说,文件(硬盘)是一种外部设备。

文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用。

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

如:"C:\Users\Desktop\newc++file.cpp"

文件标识通常称为文件名

windows和Linux允许没有后缀名的文件。

 文件类型

包含在头文件<stdio.h>中

Q:什么是文件类型?

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

 文件类型声明:

struct _iobuf {char*  _ptr;int    _cnt;char*  _base;int    _flag;int    _file;int    _charnuf;int    _butsiz;char*  _tmpfname;
};
typedef struct _iobuf FILE;

        文件类型并不是真正文件的类型,而是将硬盘文件的一些信息作为一组数据存放在内存中,CPU通过这组数据来对文件进行操作。 

        在对文件进行操作时,可以将文件看作一个FILE类型的变量,FILE类型的变量是在使用文件时,系统自动创建的。

文件指针

FILE* pf;

文件指针:定义pf是一个指向FILE类型的数据变量。通过文件指针变量能够找到与它关联的文件


 文件的打开和关闭

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

文件的使用:打开文件--->读出\写入数据--->关闭文件。

ANSIC规定使用fopen打开文件,fclose关闭文件。

函数声明:

FILE* fopen(const char* filename, const char* mode);
//filename :文件名
//mode     :打开模式int fclose(FILE* stream);
//stream   :文件指针
文件打开模式 
  • 'r'模式:这是“只读”模式。如果文件不存在,尝试打开将失败。文件指针位于文件开头,不会清空文件原有内容。

  • 'w'模式:这是“只写”模式。如果文件存在,它将被清空并从头开始写入;如果文件不存在,将创建一个新文件。文件指针位于文件开头。

  • 'a'模式:这是“追加”模式。如果文件存在,写入的数据将添加到文件末尾,不会清空原有内容;如果文件不存在,将创建一个新文件。文件指针位于文件结尾。

  • 'r+'模式:这是“读写”模式。文件必须存在,文件指针位于文件开头。可以在文件任意位置读取或写入内容,写入操作会覆盖原有位置的内容。

  • 'w+'模式:这也是“读写”模式。它类似于'w'模式,但是它允许读取操作。打开文件后,会清空文件内原有的内容。

  • 'a+'模式:这同样是“读写”模式。它类似于'a'模式,但是它允许读取操作。写入内容时,只会追加在文件尾部。

  • 还有很多操作模式,感兴趣读者自行查阅。

实例:

int main()
{//打开文件FILE* pf = fopen("test.txt", "r");//fopen的参数是:路径 、操作if (pf == NULL){perror("fopen");//perror等价于printf("open:%s\n",strerror(errno));return 1;}//文件操作//...//关闭文件fclose(pf);pf = NULL;return 0;
}

文件操作函数(顺序)

头文件<stdio.h>

0、“流”

文件操作函数中的输入输出是相对于内存而言的:

        屏幕和键盘也算一种外部设备(通过流),为什么C程序不用主动的去打开或关闭这些外部设备?

1、任何一个C程序会默认打开3个流

  • FILE* stdin  - 标准输入流 (键盘)
  • FILE* stdout  - 标准输出流 (屏幕)
  • FILE* stderr  - 标准错误流 (屏幕)
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.arr, &(s.age), &(s.score));//printf("%s %d %f", s.arr, s.age, s.score);//等价于fprintf(stdout, "%s %d %f", s.arr, s.age, s.score);fclose(pf);pf = NULL;return 0;
}

1、字符输出函数fputc

函数声明:

int fputc( int c, FILE *stream );

使用:

int main()
{//打开文件FILE* pf = fopen("test.txt", "w");// w:写入模式if (pf == NULL){perror("fopen");return 1;}//写文件char i = 0;for (i = 'a'; i < 'f'; i++){fputc(i, pf);}fclose(pf);pf = NULL;return 0;
}
2、字符输入函数fgetc 

函数声明:

int fgetc( FILE *stream );

使用:

int main()
{//打开文件FILE* pf = fopen("test.txt", "r");// r:读取模式if (pf == NULL){perror("fopen");return 1;}int ch = '\0';while ((ch = fgetc(pf)) != EOF){printf("%c\n", ch);}//关闭文件fclose(pf);pf = NULL;return 0;
}
3、字符串输出函数fputs

声明:

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

使用:

4、 字符串输入函数fgets
char *fgets( char *str, int n, FILE *stream );
//str:数据的存储位置
//n:要读取的最大字符数
//stream:文件指针

使用:

 5、格式化输入函数fscanf
int fscanf( FILE *stream, const char *format [, argument ]... );

使用:

 

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.arr, &(s.age), &(s.score));//printf("%s %d %f", s.arr, s.age, s.score);//等价于fprintf(stdout, "%s %d %f", s.arr, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
6、格式化输出函数fprintf
int fprintf( FILE *stream, const char *format [, argument ]...);

使用:

7、二进制输入函数fread
size_t fread( void *buffer, size_t size, size_t count, FILE *stream );

 使用:

int main()
{struct S s = { 0};FILE* pf = fopen("test.txt", "rb");//rb:二进制读模式if (pf == NULL){perror("fopen");return 1;}//以二进制形式写入到文件中fread(&s, sizeof(struct S), 1, pf);//读二进制数据,放在s中printf("%s %d %f", s.arr, s.age, s.score);fclose(pf);pf = NULL;return 0;
}
8、二进制输出函数fwrite
size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
//buffer:待写入数据的地址
//size:待写入数据的大小
//count:最大写入项数
//stream:指向 FILE 结构的指针

使用:

 几种输入输出

int sprintf( char *buffer, const char *format [, argument] ... );
int sscanf( const char *buffer, const char *format [, argument ] ... );

使用:

int main()
{struct S s = { "zhangsan",20,50.5f };struct S tmp;char buf[100] = { 0 };//把s中的格式化数据转化成字符串放到buf中sprintf(buf, "%s %d %f", s.arr, s.age, s.score);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;
}

补充:动态内存和文件、输入输出、流 

文件随机读写函数

1、fseek:定位指针
int fseek( FILE *stream, long offset, int origin );
//offset:偏移量
//origin:起始位置

fseek通过起始位置+偏移量的方法定位指针 。

origin由三种取值:

  • SEEK_SET:文件起始位置
  • SEEK_CUR:当前指针位置
  • SEEK_END:文件末尾
2、ftell:返回指针位置
long ftell( FILE *stream );
//返回指针相对于起始位置的偏移量
3、rewind:重定位指针
void rewind( FILE *stream );
//使指针返回到起始位置
4、实例
int main()
{FILE* pf = fopen("test.txt", "r");if (pf == NULL){perror("Fopen");return 1;}//当前文件中存放:abcdef//定位文件指针fseek(pf, -2,SEEK_END );//当前pf位置 =  -2 + SEEK_END printf("%d\n", ftell(pf));//4//相对于文件起始位置的偏移量int ch = fgetc(pf);printf("%c\n", ch); // 打印的是erewind(pf);//将指针返回到文件起始位置printf("%d\n", ftell(pf));//0fclose(pf);pf = NULL;
}

 文本文件和二进制文件

根据数据的组织形式,数据文件分为:

  • 二进制文件:把内存中的二进制数据,不加任何转化输出到外存中去,就是二进制文件。
  • 文本文件:在外存中以ASCII码的形式存放,在存储前需要转换。


 文件读取结束的判定

1、被错误使用的feof函数

feof函数:在文件读取的过程中,不能用feof函数的返回值直接用来判断文件是否结束。而是应用于当文件读取结束时,判断读取结束的原因(是遇到文件尾部结束,还是读取失败?)。

int ferror( FILE *stream );
//ferror函数用于检测文件读写过程中是否产生了错误。
int feof( FILE *stream );
//feof函数用于检测是否已到达文件末尾(EOF,End Of File的缩写)
2、判断文件是否读取结束:
  1. 文本文件读取是否结束,判断返回值是否为EOF(fgetc函数),或者为NULL。
  2. 二进制文件读取结束判断,判断返回值是否小于实际要读取的个数。

对于文本文件:

        fgetc:返回值为NULL或者EOF,表示文件读取结束,可以利用feof来对fgetc的返回值进行判断,来确定fgetc为什么读取结束。

对于二进制文件:

        fread:返回值是实际读到的元素的个数,参数count是要求读到的个数。比较返回值和count的关系,就可以判断二进制文件读取是否结束。

3、实例

 ferror和feof两者经常搭配使用:(以二进制读写为例)

enum { SIZE = 5 };int main()
{double a[SIZE] = { 1,2,3,4,5 };FILE* fp = ("test.txt", "wb");if (fp == NULL){perror("Fopen");return 1;}fwrite(a, sizeof(*a), SIZE, fp);fclose(fp);double b[SIZE];fp = fopen("test.txt", "rb");if (fp == NULL){perror("Fopen");return 1;}size_t ret_code = fread(b, sizeof(*b), 8, fp);if (ret_code == SIZE){puts("数组读取成功:");int i = 0;for (i = 0; i < SIZE; i++)printf("%f ", b[i]);putchar('\n');}else{if (!feof(fp))printf("未达到文件末尾\n");else if (ferror(fp)){printf("读取错误\n");}}fclose(fp);fp = NULL;return 0;
}

文件缓冲区

(操作系统会对缓冲区进行详细的讲解,缓冲区也有相应的库操作函数)

        文件缓冲区是内存区预留的一定空间,用以暂时存放读写期间的文件数据。其主要目的是减少读取硬盘的次数,因为硬盘的读写速度相对较慢,而内存的读写速度较快。通过缓冲区,系统可以先将数据读入内存,然后再从内存中读取或写入数据,从而减少了对硬盘的直接访问,提高了数据处理的效率。

 缓冲区存在的意义

  1. 提高数据读写效率:通过缓冲区,系统可以减少对硬盘的直接访问次数,从而提高数据读写的效率。
  2. 保护硬盘:频繁的硬盘访问会加速硬盘的磨损和老化。通过缓冲区,系统可以减少对硬盘的访问次数,从而延长硬盘的使用寿命。
  3. 提高系统稳定性:缓冲区可以平滑数据传输过程中的波动和延迟,提高系统的稳定性和可靠性。

        因为有缓冲区的存在,C语言在操作文件的时候,需要刷新文件缓冲区或在文件操作结束时关闭文件。(fclose,也会自动刷新缓冲区)。

---------------------------------------------------文件到此结束------------------------------------------------------------

         文件了解即可。其实文件没有那么重要,知道最初识得文件操作函数即可,因为在实际项目开发的时候,不可能直接对文件来进行读写,而是利用数据库来存取数据,更加高效。

http://www.lryc.cn/news/487760.html

相关文章:

  • 服务器上部署并启动 Go 语言框架 **GoZero** 的项目
  • 【Java SE 】继承 与 多态 详解
  • 【大语言模型】ACL2024论文-16 基于地图制图的罗马尼亚自然语言推理语料库的新型课程学习方法
  • 秋招大概到此结束了
  • 华为OD机试真题---字符串化繁为简
  • 概念解读|K8s/容器云/裸金属/云原生...这些都有什么区别?
  • 初识Arkts
  • 基本的SELECT语句
  • 51c自动驾驶~合集30
  • Python Tutor网站调试利器
  • h5小游戏实现获取本机图片
  • 前端 javascript a++和++a的区别
  • OceanBase V4.x应用实践:如何排查表被锁问题
  • ctfshow-web入门-SSRF(web351-web360)
  • 【日常记录-Git】如何为post-checkout脚本传递参数
  • 《机器人控制器设计与编程》考试试卷**********大学2024~2025学年第(1)学期
  • 后台管理系统(开箱即用)
  • 5G CPE与4G CPE的主要区别有哪些
  • 量化交易系统开发-实时行情自动化交易-4.1.3.A股平均趋向指数(ADX)实现
  • tcp的网络惊群问题
  • 云原生之运维监控实践-使用Prometheus与Grafana实现对Nginx和Nacos服务的监测
  • 软考教材重点内容 信息安全工程师 第 4 章 网络安全体系与网络安全模型
  • 机器学习——期末复习 重点题归纳
  • MYSQL——数据更新
  • Vite 基础理解及应用
  • [JAVA]用MyBatis框架实现一个简单的数据查询操作
  • CSS 样式的优先级?
  • Linux驱动开发快速入门——字符设备驱动(直接操作寄存器设备树版)
  • 数据结构《栈和队列》
  • C# 超链接控件LinkLabel无法触发Alt快捷键