C语言:文件操作的详解(看完一定有更深刻的理解)

目录

前言

程序文件

文件的打开和关闭

 标准流

文件的顺序读写

写文件

fputc函数

fputs函数

fprintf函数

读文件

fgetc函数

fgets函数

fscanf函数

printf/fprintf/sprintf

scanf/fscanf/sscanf

文件的随机读写

fseek函数

ftell函数

rewind函数

大多数人用错的feof函数

文件缓冲区

为什么要这么设计呢?


前言

在我们进行C语言编程时,我们每次运行的代码虽然都会出结果,但是我们并不能保存下来,所以如果我们需要保存下来得学习文件的操作

文件几乎是每个项目里都需要的东西,因为我们的项目运行的时候都需要保存数据,甚至需要从文件里读取数据,比如一个学生管理系统做完之后,使用者总需要用这个系统将所有学生的信息输入到电脑里,但是总不能输入不保存吧,所以需要使用一个文件保存,在下次使用的时候又可以从这个文件里读取数据,从而进行增删改查等等功能

 所以文件的学习是非常有必要的

程序文件

程序文件有多种,后缀为.c的是源程序文件,后缀为.obj的是目标文件,后缀为.exe的是可执行程序

我们写代码的文件就是后缀为.c的文件,经过一系列的编译环节,会生成一个后缀为.obj的目标文件,最后链接形成后缀为.exe的可执行程序

文件的打开和关闭

先来看看具体代码如何实现

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}

	fclose(fp);
	return 0;
}

这里定义了一个文件指针FILE* fp来接收后面fopen函数的返回值

FILE* 是一个类型

通过上图可以发现,fopen返回的是一个FILE*类型的指针,所以我们需要创建一个FILE*类型的指针接收它的返回值

里面有两个参数,第一个是需要打开的文件名,第二个是想要打开的模式

这里的mode重点了解两个,一个是上面写的"w",一个是"r" 

 

记忆方法:
r是read

w是write

上面写的"w"是以写的方式打开文本,所以后续的操作肯定是要往文件里面写数据,如果上面是"r",则后续的操作肯定是要往文件里面读数据

以写的方式打开文件时,若该文件名不存在,则会自动创建一个文件

每次以写的形式打开文件时,若是原先该文件存在并且有内容,该内容会被清空

如果文件打开成功,则在后面我们可以用它的返回值对这个文件进行操作

如果文件打开失败,则会返回NULL指针

看到这里其实可以知道,它需要判断文件指针是否为空,与上一章的动态内存开辟类似 

所以我们后面写了个if 语句判断fp是否为空

如果不为空代表文件打开成功,下面则可以进行文件的操作

如果为空,则使用了perror函数打印错误信息,然后退出程序

当我们文件打开成功后,进行了一系列的操作之后,我们是需要把文件关闭的

文件的打开数量是有限制的,在进行一些大程序需要多个文件的时候若不进行关闭文件的操作是会影响程序的运行的,所以我们要养成关闭文件的好习惯

在退出程序之前,写了一句fclose函数

函数的返回值为int类型,若关闭成功,则返回0,失败则返回EOF(-1) 

它的参数是一个文件指针,里面放的名字是个stream(流)

这个文件指针就是我们打开文件时的返回值 fp,fp这个文件指针在后面对文件进行读写操作时都是非常重要的

我们只需要把打开的那个文件指针放到fclose的参数中就能将文件进行关闭了

流是个抽象的概念,我们的程序的数据需要输出到各种外部设备,也要从各种外部设备获取数据,但是不同的设备所需要的操作各不相同,为了方便程序员对各种外部设备的传输,抽象出了流的概念,流可以帮助我们完成这一系列的传输作用,我们可以把它想象成一个流水一般将一个个物品流动到其他地方

 标准流

我们在从键盘上输入数据,向屏幕上输出的时候,我们并没有打开什么流

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

stdin标准输入流
stdout标准输出流
stderr标准错误流

scanf就是从标准输入流中读取数据

printf就是将信息输出到标准输出流中

错误信息通过标准错误流输出到屏幕中

这三个流 stdin,stdout,stderr也是文件指针类型

在C语言中,就是通过FILE*的文件指针来维护流的各种操作的

文件的顺序读写

写文件

既然要写文件,那么上面fopen的mode当然得是"w"

这时候又要了解几个函数了

fputc函数

f表示文件file

put表示输出

c表示一个字符 

函数成功执行后,返回的是输入到文件里字符的ascii码值

如果执行失败,返回EOF(-1)

第一个参数是我们要写入的字符,第二个参数是打开的文件指针

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputc('a', fp);
	fclose(fp);
	return 0;
}

 上面的函数可以将一个字符a写入到test.txt文件中

最终结果就是会在当前项目的文件夹下出现一个test.txt文件并且里面有一个字符a

如果想要利用它打印多个字符,可以加入for循环试试

提示:利用fputc读取失败后返回的EOF终止循环

fputs函数

f表示文件file

put表示输出

s表示一个字符串

返回值和第二个参数和上面的fputc一致,唯一的区别就是第一个参数 

前面的fputc是写入的一个字符,那这个当然就是写入一个字符串了

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputs("abcdef", fp);
	fclose(fp);
	return 0;
}

最终结果如下:

fprintf函数

它的使用方法和printf几乎相同,只需要在首个参数加入文件指针即可

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	int n = 10;
	fprintf(fp, "n = %d", n);
	fclose(fp);
	return 0;
}

 上面的函数参数中第一个参数传了打开文件的文件指针,后面的就和正常的printf打印到屏幕上一致即可

最终结果:

前面有写到标准流中printf是将内容打印到标准输出流中

那么我们的第一个参数如果给标准输出流(stdout)会发生什么?

fprintf(stdout, "n = %d", n);

 

最终也是向屏幕上打印了和文件中一致的内容

所以说printf默认使用的就是标准输出流,而fprint适用所有流

读文件

既然要开始读取文件了,那么fopen里的mode应该改成"r"

fgetc函数

 f表示文件file

get表示输入

c表示一个字符 

 它的返回值与fputc一致

函数参数只需要一个文件指针

这个函数会默认从你给的文件中的第一个字符开始读入数据

现在假设我的test.txt中的内容为

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ret = fgetc(fp);
	printf("%c\n", ret);
	fclose(fp);
	return 0;
}

 代码的最终结果如下:

因为第一个字符为a,所以最终的返回值为a放入ret中,最后就可以打印出来

这个时候如果程序并没有结束,那么我们如果再使用一次fgetc它会读取到第二个数据b

每读取一个数据它就会自动指向下一个数据

所以我们如果要获取文件内的全部数据,可以使用循环来一个个读取放入ret中并打印

fgets函数

 f表示文件file

get表示输入

s表示一个字符串 

 第一个参数str表示要存入文本文件数据的字符串,第二个参数为存入的数据个数,第三个就是打开文件的文件指针

如果读取成功,返回str

如果读取失败,返回NULL指针

假设test.txt的内容还是和前面一样

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	char str[10];
	fgets(str, 5, fp);
	printf("%s\n", str);
	fclose(fp);
	return 0;
}

上面我们将读取文件中的内容放入str数组中,最后打印str结果如下:

明明要读取的是5个数据,怎么最后打印出来只有4个数据?

别忘了字符串结尾还需要有一个'\0'

所以读入的是abcd和'\0'

fscanf函数

 它和fprintf一样,也是和scanf的用法近乎相同,但是多了一个参数就是流

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	char str[10];
	fscanf(fp, "%s", str);
	printf("%s\n", str);
	fclose(fp);
	return 0;
}

最终结果:

 

直接就可以将文本中第一行给全部读取

如果想读取下一行,可以再次使用一遍fscanf

我们利用这些读文件的函数读取时,它们是可以将回车一起读取的!

printf/fprintf/sprintf

三者的用法相同,只是作用的对象不同,printf和fprintf想必已经有一些了解了,就剩下最后的sprintf了

sprintf针对的是字符串

#include<stdio.h>

int main()
{
	char str1[10] = "abcdefgh";
	char str2[10];
	sprintf(str2, "%s", str1);
	printf("%s\n", str2);
	return 0;
}

第一个参数str是要输出的地方,然后后面和平常的printf的格式一致

最终结果:

scanf/fscanf/sscanf

它们也是和上面的printf家族一样,下面来看看sscanf函数

第一个参数s是作为数据库一样的功能,后面则是要写入数据的格式,和平时我们使用scanf一样

#include<stdio.h>

int main()
{
	char str1[10] = "abcdefgh";
	char str2[10];
	sscanf(str1, "%s", str2);
	printf("%s\n", str2);
	return 0;
}

 最终结果:

通过上面函数的对比可以得出

printf和scanf默认的流是标准输入(输出)流,所以我们第一个参数并不需要自己输入

sprintf和sscanf需要的流是字符指针,我们需要传递一个字符指针

而fprintf和fscanf可以给任何流,它们比较全面,我们可以拿它做很多事情,但常用的还是文件相关的方面

文件的随机读写

fseek函数

该函数的作用是将我们的文件指针放置到我们想要放置的地方进行读取

第一个参数肯定已经不陌生了

第二个参数表示我们需要的偏移量

可以理解为向后走offset个数据

第三个参数,origin(来源)表示我们要从哪里开始偏移offset个数据

下面是可以放入第三个参数中的三种写法

 

直接看代码

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	fseek(fp, 3, SEEK_SET);
	char ret = fgetc(fp);
	printf("%c", ret);
	fclose(fp);
	return 0;
}

test.txt依旧是和之前一样 

我们在读取前先让文件指针从SEEK_SET(文件开头)开始偏移3个数据

那么如果我们从中读取会先从开头向后数三个最后从第四个开始读取

所以最终结果如下:

ftell函数

返回文件指针相对于起始位置的偏移量,需要我们接收它的返回值

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	for (int i = 0; i < 3; i++)
	{
		fgetc(fp);
	}
	int ret = ftell(fp);
	printf("%d", ret);
	fclose(fp);
	return 0;
}

我们先用一个循环让fgetc读取三次,这时候已经距离起始位置是3了,这时候我们用ftell就能够得到3

 

rewind函数

它可以让文件指针回到起始位置

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}
	for (int i = 0; i < 3; i++)
	{
		fgetc(fp);
	}
	rewind(fp);
	char ret = fgetc(fp);
	printf("%c", ret);
	fclose(fp);
	return 0;
}

我们先让fgetc读取3个数据,下面如果我们还要使用fgetc读取的时候应该是读取第4个数据

如果我还要拿到第1个数据的话我们就直接用rewind函数再读取

所以最终结果如下:

 

大多数人用错的feof函数

很多人都会拿它来判断是否读取要了文件末尾

但它的实际作用是当文件无法读取之后,读取文件失败的原因是否是遇到了文件末尾 

文件缓冲区

我们在读取数据或者写入数据的时候,并不是直接就写入硬盘中的,而是要先进入各自的缓冲区,当缓冲区满时或者收到特定指令时,就会传给硬盘

为什么要这么设计呢?

如果我们写入一个数据就直接传入硬盘的话,那么我们需要传给硬盘多少次呢?

硬盘可以帮你解决问题,但它并不希望被频繁打扰

如果是你,你会希望被人打扰无数次还是被人打扰几次?


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/493731.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据库管理操作】Mysql 创建学生数据库及对数据表进行修改

MySQL 创建学生成绩数据库 1.创建数据库 create database studentscore;创建完成之后&#xff0c;如果需要使用该数据&#xff0c;使用use命令 use studentscore;创建表前查看当前数据库中包含的表 show tables; 2.创建bclass表 create table bclass( class_id char(8) …

深度学习入门1——Optimization

Methods of optimization Stochastic Gradient Descent (SGD) use mini-batch (32/64/128) to do gradient descent SGD Momentum continue moving in the general direction as the previous iterations Build up “velocity” as a running mean of gradients Rho giv…

全国河流湖库公开数据及应用实践

关于全国河流湖口的数据&#xff0c;通常指的是各条河流流入湖泊或海洋的位置及其相关的水文、地理信息。这类数据包括但不限于以下几个方面&#xff1a; 1. 地理位置&#xff1a;每条河流的出海口或流入湖泊的具体经纬度坐标。 2. 水文特征&#xff1a;如湖口水位、流量、径…

【数据库】表的约束

目录 一、非空约束 二、主键约束 三、外键约束 四、检查约束 五、唯一性约束 一、非空约束 每个字段都要有一个是否为nul值的选择&#xff0c;这就是对数据表中将来的数据提出的约束条件。null(允许空值)&#xff1a;表示数值未确定&#xff0c;并不是数字“0”或字符“…

Avalonia笔记2 -数据集合类控件

学习笔记&#xff1a; 1. DataGrid 笔记1中已经记录&#xff1b; 2. ItemsControl 属性&#xff1a; ItemsSource&#xff1a;数据源 ItemsControl.ItemTemplate&#xff1a;单项数据模板&#xff0c;内部使用<DataTemplate> 示例&#xff1a; <ItemsContr…

学习使用xbox手柄控制小乌龟节点移动

使用xbox手柄控制小乌龟&#xff0c;首先要下载joy功能包&#xff0c;发布sensor_msgs话题也就是手柄和ros通信的话题。 下载的步骤就根据官方文档即可 joy/Tutorials/ConfiguringALinuxJoystick - ROS Wiki 这里我提供一下具体步骤 第一步 安装joy 首先安装对应系统版本的…

第十四届蓝桥杯(C/C++ 大学B组)

试题 A&#xff1a;日期统计 #include <bits/stdc.h> using namespace std;const int numbers[100] {5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7, 5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5,8, 6, 1, 8, 3, 0, 3, 7, 9, 2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, …

全局UI方法-弹窗一警告弹窗(AlertDialog)

1、描述 显示警告弹窗组件&#xff0c;可设置文本内容与响应回调。 2、属性 名称参数类型参数描述showAlertDialogParamWithConfirm | AlertDialogParamWithButtons定义并显示AlertDialog组件。 2.1、AlertDialogParamWithConfirm对象说明&#xff1a; 参数名称参数类型必填…

已注册的商标别忘了续展,新注可能难下证!

近期普推知产老杨遇到好几个网友和看过多个案例&#xff0c;以前商标名称可以申请注册下来&#xff0c;但是换字体注册不下来了&#xff0c;有的是不想续展想直接换字体申请注册&#xff0c;但是也没有下来。 这些商标名称主要是存在禁止注册或缺显&#xff0c;比如“柳林”以前…

对下载软件/文件进行校验的工具(Checksum and GPG)

前言 之前装软件一直都没有验证安装文件的习惯&#xff0c;信息安全意识不高&#xff0c;碰巧最近没啥事&#xff0c;微微写篇文章记录下校验工具&#xff08;互联网http、https、ftp 服务并没有那么安全&#xff0c;是可以被劫持篡改。老装软件选手了&#xff0c;是该养成个校…

Mac上的Gatekeeper系统跟运行时保护

文章目录 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件。如何解决&#xff1f; 参考资料门禁运行时保护 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件…

瑞_23种设计模式_观察者模式

文章目录 1 观察者模式&#xff08;Observer Pattern&#xff09;1.1 介绍1.2 概述1.3 观察者模式的结构1.4 观察者模式的优缺点1.5 观察者模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 JDK中提供的观察者模式实现 ★4.1 Observable类4.2 Obse…

修复ubuntu引导

一、制作ubuntu启动U盘 进入启动盘后&#xff0c;点击Try ubuntu&#xff0c;进入U盘的ubuntu系统。 二、配置和添加源 sudo add-apt-repository ppa:yannubuntu/boot-repair && sudo apt-get update三、运行 Boot Repair重新制作引导 sudo boot-repair注意&#x…

使用OpenXlab下载数据集(推荐)

OpenXLab浦源面向人工智能领域开发者和使用者,提供一站式AI开发平台。提供包括应用开发,模型免费托管,数据集下载等服务 官方网址:https://openxlab.org.cn/datasets?lang=zh-CN 提供了6622种数据集, 涉及计算机视觉、自然语言处理、多模态、通用机器学习、音频识别以及其他…

Bash and a Tough Math Puzzle 线段树维护区间gcd

还是一道很不错的题目&#xff0c;很容易想到用一棵线段树来维护区间gcd 注意用倍数来剪枝就好了&#xff0c;很是一到很好的题目的 #include<iostream> #include<vector> using namespace std; const int N 5e510; int n,q; struct Segment{int l,r;int d; }tr[…

Kubeflow文档1:介绍与架构

Kubeflow 2024/3/19版本的文档 此专栏用来展示相关的内容翻译&#xff0c;重点关注本地部署&#xff0c;关于运营商的方案&#xff0c;请自行查阅 文档地址https://www.kubeflow.org/docs/ 开始编辑时间&#xff1a;2024/3/27&#xff1b;最后编辑时间2024/3/27 Kubeflow文…

记录echarts各种地图json文件下载地址

今日绘图需要用到echarts的地图json文件&#xff0c;但是github上已经找不到了&#xff0c;后发现伟大的网友提供了地址如下&#xff1a;Index of /examples/data/asset/geohttps://echarts.apache.org/examples/data/asset/geo/ 免费下载实时更新的geoJson数据、行政区划边界…

【正点原子FreeRTOS学习笔记】————(4)FreeRTOS中断管理

这里写目录标题 一、什么是中断&#xff1f;&#xff08;了解&#xff09;二、中断优先级分组设置&#xff08;熟悉&#xff09;三、中断相关寄存器&#xff08;熟悉&#xff09;四、FreeRTOS中断管理实验&#xff08;掌握&#xff09; 一、什么是中断&#xff1f;&#xff08;…

leetCode刷题 20. 有效的括号

目录 题目&#xff1a; 1. 思路 2. 解题方法 3. 复杂度 4. Code 题目&#xff1a; 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型…

docker部署音乐播放下载器

可播放及下载音乐的工具 musicn 下载镜像 docker pull ghcr.m.daocloud.io/wy580477/musicn-container:latest创建数据目录 mkdir -p /data/musicdocker-compose部署 vim docker-compose.yml version: 3 services:musicn:container_name: musicnimage: ghcr.io/wy580477/m…
最新文章