C语言——文件操作

2023-11-10

使用文件的原因

当在程序中输入一些内容并保存时,如果程序关闭,内容就会消失。为了杜绝此类事情发生,我们可以使用文件,将数据持久化,把数据放在磁盘文件或数据库中,只有我们选择自己删除数据的时候,数据才会消失,否则数据会一直保存,使用文件可以将数据直接存在电脑的硬盘上,使数据持久化。此篇博客主要讨论数据文件。

文件

磁盘上的文件是文件,但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)

程序文件

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

数据文件

文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件或者输出文件的内容
在这里插入图片描述

文件名

一个文件有一个唯一的文件标识,文件名包含3个部分:文件路径+文件名主干+文件后缀,例如:c:\program\text.txt ,包含路径使相同的文件名更好区分

文件的打开和关闭

文件指针

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

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
       };
typedef struct _iobuf FILE;//FILE重命名产生的结构体类型

在这里插入图片描述
1.不同的C编译器的FILE类型包含的内容不完全相同,但是大同小异
2.每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。一般都是通过一个FILE的指针 FILE* pf 来维护这个FILE结构的变量,这样使用起来更加方便
3.定义pf是一个指向FILE类型数据的指针变量。可以使pf指向某个文件的文件信息区(是一个结构体变量)。通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件

在这里插入图片描述

文件的打开和关闭

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

参数:filename:C 字符串,要打开的文件的名称
mode:以什么方式打开
返回值:如果文件已成功打开,该函数将返回一个指向 FILE 对象的指针,该指针可用于在将来的操作中标识流。否则,将返回空指针
注意:1.文件在读写之前应该先打开文件,在使用结束之后应该关闭文件
2.在打开文件同时,会返回一个FILE*的指针变量指向该文件

int fclose ( FILE * stream )

参数:stream 指向指定要关闭的流的 FILE 对象的指针
返回值:如果流成功关闭,则返回零值。失败时,返回 EOF。
注意:1.使用完fclose后,要将指向FILE的指针置为空指针NULL

打开方式:

文件使用方式 含义 如果指定文件不存在
"r"(只读) 为了输入数据,以读模式打开一个已经存在的文本文件 出错
"w"(只写) 为了输出数据,以写模式打开,清空现有的文件长度,如果文件不存在,则创建一个新的文件 建立一个新文件
"a"(追加) 以写模式打开文件,会在现有文件末尾添加内容,如果文件不存在,则创建一个新的文件 建立一个新文件
"r+"(读写) 以更新模式打开文件,可以读和写
"w+" 以更新模式打开文件,可以读和写,如果文件存在,清空现有的文件长度,如果文件不存在,则创建一个新的文件
"a+" 以更新的模式打开文件,可以读和写,在现有文件末尾添加内容,如果文件不存在,则创建一个新的文件,可以读取整个文件,但是只能从末尾添加内容
“rb”(只读)、“wb”(只写)、“ab”(追加)、“rb+”(读写)、“wb+”(读写)、“ab+”(读写) 与以上相关模式类似,不过是以二进制模式,而不是文本模式打开文件

打开方式:

#include <stdio.h>
int main()
{
	FILE* pf = fopen("project.txt", "w");//前提是有此文件,否则会报错

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	fputs("Hello World!", pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:
在这里插入图片描述

1.默认创建的project.txt在该程序所对应的文件夹下,是相对路径
2.如果没有这个project.txt,以r的方式打开,程序会报错No such file or directory

文件的顺序读写

函数 函数名 适用 补充
字符输入函数 fgetc 所有输入流
字符输出函数 fputc 所有输出流
文本行输入函数 fgets 所有输入流
文本行输出函数 fputs 所有输出流
格式化输入函数 fscanf 所有输入流
格式化输出函数 fprintf 所有输出流
二进制输入 fread 文件
二进制输出 fwrite 文件

fputc:

#include <stdio.h>
int main()
{
	FILE* pf = fopen("project.txt", "w");//前提是有此文件,否则会报错

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件
	int i = 0;
	for (i = 'a'; i <= 'z'; i++)
	{
		fputc(i, pf);
	}

	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
1.1.fputc:向文件中输出字符,一次写一个
int fputs ( const char * str, FILE * stream ):将 str 所指向的 C 字符串写入流
成功后,将返回非负值。
出错时,该函数返回 EOF 并设置错误指示器 (ferror)

fgetc:

#include <stdio.h>
int main()
{
	FILE* pf = fopen("project.txt", "r");//前提是有此文件,否则会报错

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	int ret = fgetc(pf);//单个读
	printf("%c ", ret);

	ret = fgetc(pf);
	printf("%c ", ret);

	ret = fgetc(pf);
	printf("%c \n", ret);

	int ch = 0;
	while ((ch = getc(pf)) != EOF)//一块读
	{
		printf("%c ", ch);
	}

	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
1.向程序中输入字符,一次读一个
2.int fgetc ( FILE * stream ):从流中获取字符
成功后,将返回读取的字符(提升为 int 值)。
返回类型为 int 以适应特殊值 EOF,这表示失败

fputs:

#include <stdio.h>
int main()//写一行数据
{
	FILE* pf = fopen("project.txt", "a");//w会清空之前的内容,用a会追加

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写文件

	fputs("Keep Running ",pf);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
1.将 str 所指向的 C 字符串写入流
2.成功后,将返回非负值。出错时,该函数返回 EOF 并设置错误指示器 (ferror)
3.以w的模式写,会清空之前的内容,以a的模式写,会追加在之前的内容后

fgets:

#include <stdio.h>
int main()//读一行数据
{
	FILE* pf = fopen("project.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文件
	char arr[10];
	fgets(arr, 4, pf);
	printf("%s", arr);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
1.虽然读的是4个,实际是3个,还有一个’\0’
2.从流中读取字符并将其作为 C 字符串存储到 str 中,直到读取 (num-1) 字符或达到换行符或文件末尾(以先发生者为准)

fprintf:

#include <stdio.h>

typedef struct STU
{
	char name[20];
	int age;
	double score;
}S;

int main()//以格式化形式输出数据
{
	S stu = { "张三",18,90.0f };
	FILE* pf = fopen("project.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//写到文件中
	fprintf(pf, "%s %d %f", stu.name, stu.age, stu.score);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
1.以格式化的形式输出到文件中去
2.将格式所指向的 C 字符串写入流。如果 format 包含格式说明符(以 % 开头的子序列),则 format 后面的其他参数将被格式化并插入到生成的字符串中,以替换它们各自的说明符

fscanf:

#include <stdio.h>

typedef struct STU
{
	char name[20];
	int age;
	double score;
}S;

int main()//以格式化形式输入数据
{
	S stu = { 0 };
	FILE* pf = fopen("project.txt", "r");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读文中
	fscanf(pf, "%s %d %lf", stu.name, &(stu.age), &(stu.score));

	printf("%s %d %lf", stu.name, stu.age, stu.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
1.从流中读取数据,并根据参数格式将其存储到附加参数所指向的位置

总结

在这里插入图片描述
任何一个c程序,只要运行起来,就会默认打开三个流,都是FILE*类型:
stdin:标准输入流(键盘)
stdout:标准输出流(屏幕)
stderr:标准错误流(屏幕)
所以使用scanf和printf时,可以直接使用,不需要打开屏幕,而写文件则需要打开文件获取这个流,然后写或读数据
而fprintf适用于所有输出流,故可以在屏幕上打印,如下图所示
在这里插入图片描述

fwrite:

#include <stdio.h>

typedef struct STU
{
	char name[20];
	int age;
	double score;
}S;

int main()
{
	S stu = { "张三",18,100.0f };
	//以二进制形式写到文件中
	FILE* pf = fopen("project.txt", "wb");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制方式写
	fwrite(&stu, sizeof(stu), 1, pf);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述
因为是以二进制的形式写的,所以打开文件后是乱码
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream )
ptr:要写的元素来自哪里,指向要写入的元素数组的指针
size:要写入的每个元素的大小(以字节为单位)
count:元素数,每个元素的大小为字节大小
count=1 ,一次读1个结构体大小的数据,相当于读一次结构体中的所有内容

fread:

#include <stdio.h>

typedef struct STU
{
	char name[20];
	int age;
	double score;
}S;

int main()
{
	S stu = { 0 };
	FILE* pf = fopen("project.txt", "rb");

	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//二进制方式读
	fread(&stu, sizeof(stu), 1, pf);
	printf("%s %d %lf", stu.name, stu.age, stu.score);
	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;
}

输出结果:

在这里插入图片描述

sfrintf:

#include <stdio.h>

typedef struct STU
{
	char name[20];
	int age;
	double score;
}S;

int main()
{
	S stu = { "张三",18,90.0f };
	S stu1 = { 0 };
	char buf[100] = { 0 };
	//把stu中的格式化数据转化成字符串放到buf中
	sprintf(buf, "%s %d %lf", stu.name, stu.age, stu.score);
	printf("%s",buf);
	printf("\n");
	//从字符串buf中获取一个格式化的数据到stu1中
	sscanf(buf, "%s %d %lf", stu1.name, &(stu1.age), &(stu1.score));
	printf("%s %d %lf", stu1.name, stu1.age, stu1.score);
	return 0;
}

输出结果:

在这里插入图片描述

1.此时buf中的内容已经变成了字符串"张三 18 90.000000",所以用%s打印
scanf:是针对标准输入的格式化输入语句
printf:是针对标准输出的格式化输出语句
fscanf:是针对所有输入流的格式化输入语句
fprintf:是针对所有输出流的格式化输出语句
ssanf:从一个字符串中转化成一个格式化的数据
ssprintf:把一个格式化的数据转化成字符串

文件的随机读写

fseek

根据文件指针的位置和偏移量来定位文件指针

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

offset:要从源偏移的字节数
origin的三个参数

SEEK_CUR :文件指针当前位置
SEEK_END :文件结尾
SEEK_SET :文件开头

ftell

返回文件指针相对于起始位置的偏移量

long ftell(FILE* stream );

rewind

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

void rewind ( FILE * stream );

下面代码是关于以上三个函数的使用

#include <stdio.h>

int main()
{
	char arr[10] = { 0 };
	FILE* pf = fopen("project.txt", "r");//文件中已有ABCDEF
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//读取文件
	int ch = fgetc(pf);
	printf("%c\n", ch);//A

	//调整文件指针
	fseek(pf, 2, SEEK_SET);//相对于起始位置偏移量
	ch = fgetc(pf);
	printf("%c\n", ch);//C

	fseek(pf, 2, SEEK_CUR);//执行完上术代码,已经指向D,D的偏移量是0
	ch = fgetc(pf);
	printf("%c\n", ch);//F,然后指向F后
	int ret = ftell(pf);
	printf("%d\n", ret);//相对于起始位置的偏移量6

	fseek(pf, -3, SEEK_END);//相对于最后位置偏移量
	ch = fgetc(pf);
	printf("%c\n", ch);//D
    ret = ftell(pf);//然后指向E
	printf("%d\n", ret);//相对于起始位置的偏移量4

	//回到起始位置
	rewind(pf);
	fgets(arr, 7, pf);
	printf("%s", arr);

	//关闭文件
	fclose(pf);
	pf = NULL;
	return 0;

输出结果:
在这里插入图片描述

文本文件和二进制文件

  • 数据文件被称为文本文件或者二进制文件,数据在内存中以二进制的形式存储,如果不加转换的输出到外存,就是二进制文件,如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件
  • 字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储

文件读取结束的判定

  feof:判断结束的原因,已经结束了,读到文件尾结束
  ferror:判断结束的原因,已经结束了,读的时候是否遇到错误而结束

  • 文本文件读取是否结束,判断返回值是否为 EOF ( fgetc ),或者 NULL ( fgets )例如:fgetc 判断是否为 EOF .fgets 判断返回值是否为 NULL
  • 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。例如:fread判断返回值是否小于实际要读的个数

文件缓冲区

ANSIC 标准采用“缓冲文件系统”处理的数据文件的,所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘上。如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(程序变量等)。缓冲区的大小根据C编译系统决定的,因为有缓冲区的存在,C语言在操作文件的时候,需要做刷新缓冲区或者在文件操作结束的时候关闭文件。如果不做,可能导致读写文件的问题
在这里插入图片描述
只有缓冲区放满才能读和写

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言——文件操作 的相关文章

随机推荐

  • 数组新增的常用方法(es6-es12)-今天一定要学会

    1 forEach 遍历数组中的元素 不改变原数组 2 map 遍历数组 对数组中每个元素做操作并将操作后的元素放到数组中返回 不改变原数组 3 filter 过滤 返回包含所有在回调函数上结果未true的值的新数组 不改变原数组 4 ev
  • 关于CPU的浮点运算能力计算

    原文链接 https www jianshu com p b9d7126b08cc Intel官方参数 https ark intel com FLOAS 核数 单核主频 CPU单个周期浮点计算值
  • 从头开始学Java——JVM虚拟机八问

    文章目录 什么是Java虚拟机 为什么Java被称为 平台无关的编程语言 什么是JIT HotSpot怎么工作的 HotSpot虚拟机要使用解释器与编译器并存的架构 什么是编译时 运行时 编译 运行 编译时运行时问题归纳 反射 描述Java
  • 解决node-sass: Command failed 问题

    从github 下载的vue 源码 yarn 安装报错 爬了下百度 试了好几种方法都没成功 最后ChatGPT帮我解决了 ChatGPT回答 一语命中 好用 node sass 是一个将 Sass 编译为 CSS 的 Node js 模块
  • 关于springboot使用定时器的几种方式

    1 Scheduled注解 Component public class SimpleSchedule private Integer time 0 定时器定义 设置执行时间 Scheduled cron 6 private void pr
  • maven -- 问题解决(二)解决“Could not calculate build plan”问题

    错误提示如下 eclipse maven Could not calculate build plan Failure to transfer org apache maven plugins maven surefire plugin p
  • disabled_button

    直接看题目 然后干他 这里说flag在按钮上 但是我们就是按不了这个flag 那直接看HTML呗 可以看到它这里有个搞鬼的东东 一搜 可以知道这个东西禁止让你输入 你不让我输入 我就给你删了呗 然后我们可以点flag 一点 flag就出来了
  • 关于漏洞"这个页面包含一个错误/警告信息,可能会导致敏感信息泄露"

    公司开发的产品 在用软件扫描漏洞时 扫出了这么一个漏洞 可以看出有漏洞的地方是登录页面 在登录中 主要逻辑如下 一些拒绝登录是通过抛异常 gt 然后捕获异常 gt 获取异常信息 gt 跳回到登录页面并展示错误信息 下面为代码示例 Requs
  • 如何获取 docker 容器(container)的 ip 地址

    1 进入容器内部后 lcc lcc docker exec it docker mysql bash root b3b1d61142df root b3b1d61142df cat etc hosts 127 0 0 1 localhost
  • Hive 是怎样保存元数据的

    保存元数据的方式有 内存数据库derby 本地mysql数据库 远程mysql数据库 本地的mysql数据库用的比较多 因为本地读写速度都比较快 内存数据库derby 安装小 但是数据存在内存 不稳定 mysql数据库 数据存储模式可以自己
  • React 中的元素、组件、实例和节点

    React 元素其实就是一个简单JavaScript对象 一个React 元素和界面上的一部分DOM对应 描述了这部分DOM的结构及渲染效果 一般我们通过JSX语法创建React 元素 React 元素可以分为两类 DOM类型的元素和组件类
  • Java常用API

    文章目录 1 API 1 1 API概述 帮助文档的使用 1 2 键盘录入字符串 2 String类 2 1 String概述 2 2 String类的构造方法 2 3 创建字符串对象的区别对比 2 4 字符串的比较 2 5 String方
  • Docker常用镜像命令

    帮助命令 查看docker版本 docker version 对docker信息的描述 docker info docker的帮助命令 docker help 详解 docker OPTIONS COMMAND docker的命令公式 镜像
  • Text to image论文精读MISE:多模态图像合成和编辑Multimodal Image Synthesis and Editing: A Survey

    由于信息在现实世界中以各种形式存在 多模态信息之间的有效交互和融合对于计算机视觉和深度学习研究中多模态数据的创建和感知起着关键作用 近期 OpenAI 发布的 DALLE 2 和谷歌发布的 Imagen 等实现了令人惊叹的文字到图像的生成效
  • 「经验分享」一款PCB电路板设计要经历哪些流程?

    一款PCB电路板设计主要包括明确产品需求 硬件系统设计 器件选型 PCB绘制 PCB生产打样 焊接调试等步骤 那么这些步骤都有哪些需要注意点呢 小编带你细品 一定要细品 一 明确需求 在一个硬件设计之初 首先要做的是明确产品的需求 这里主要
  • BMP文件格式详解(BMP file format)

    BMP文件格式 又称为Bitmap 位图 或是DIB Device Independent Device 设备无关位图 是Windows系统中广泛使用的图像文件格式 由于它可以不作任何变换地保存图像像素域的数据 因此成为我们取得RAW数据的
  • 【SIMULINK】基于DQ0模型的三相异步电机自制仿真模型教程

    SIMULINK 基于DQ0模型的三相异步电机自制仿真模型 其实 打开simscape自带的异步电机模型 里面也是基于DQ0的 电机的模型定子电压作为输入 定子电流是输出 内部结构 omega 1 是DQ坐标系的转速 为0时退化为 alph
  • 计算机是人类的好伴侣 作文,书是我们的好伴侣_我和书的故事作文

    书 大家是并不陌生的 它会让人陶醉享受 也会使人沉迷于此 总得来说 书 是我们的好伴侣 不管是休闲娱乐 还是读后写作都少不了它 说到我喜欢的书 我还是算得上是个小小小的 书迷 但是我最喜欢沈石溪所写的动物小说 内容精彩而又丰富 把这所有的动
  • Android性能分析和优化之traces.txt(ANR分析)

    ANR 类型分类 1 KeyDispatchTimeout 5 seconds 主要类型按键或触摸事件在特定时间内无响应 按键或者触摸引起的ANR的时间定于是在AMS中 static final int KEY DISPATCHING TI
  • C语言——文件操作

    C语言文件操作 使用文件的原因 文件 程序文件 数据文件 文件名 文件的打开和关闭 文件指针 文件的打开和关闭 文件的顺序读写 文件的随机读写 fseek ftell rewind 文本文件和二进制文件 文件读取结束的判定 文件缓冲区 使用