文件操作(你真的会读写文件吗?)

news2024/11/16 15:41:07

文章目录

  • 一、为什么使用文件?
  • 二、什么是文件?
    • 2.1 程序文件
    • 2.2 数据文件
    • 2.3 文件名
  • 三、二进制文件和文本文件
    • 3.1 二进制文件
    • 3.2 文本文件
  • 四、文件的打开和关闭
    • 4.1 流和标准流
      • 4.1.1 流
      • 4.1.2 标准流
    • 4.2 文件指针
    • 4.3 fopen和fclose
  • 五、文件的顺序读写
    • 5.1 顺序读写函数
      • 5.1.1 fgetc和fputc
      • 5.1.2 fgets和fputs
      • 5.1.3 fscanf和fprintf
      • 5.1.4 fread和fwrite
    • 5.2 对比一组函数
  • 六、文件的随机读写
    • 6.1 fseek
    • 6.2 ftell
    • 6.3 rewind
  • 七、文件读取结束的判定
    • 7.1 是否结束的判定
    • 7.2 结束原因的判定
  • 八、文件缓冲区

欢迎各位小伙伴关注我的专栏,和我一起系统学习C语言,共同探讨和进步哦!

学习专栏:

《零基础学C语言》


一、为什么使用文件?

如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的保存,我们可以使用文件。

二、什么是文件?

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

2.1 程序文件

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

2.2 数据文件

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


我们讨论的重点就是对数据文件进行操作!


2.3 文件名

一个文件要有一个唯一的文件标识

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

  • 例如: c:\code\test.txt

为了方便起见,文件标识常被称为文件名

三、二进制文件和文本文件

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

3.1 二进制文件

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

3.2 文本文件

如果转换为以ASCII字符的形式存储的文件,就是文本文件。


那么,为什么要分两种不同文件存储呢?这就关系到数据在内存中的存储。

字符一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以使用二进制形式存储。

  • 如有整数10000,如果以ASCII码的形式输出到磁盘,则磁盘中占用5个字节(每个字符⼀个字节),而二进制形式输出,则在磁盘上只占4个字节

所以使用二进制文件可以节省内存空间,但是文本文件的优势在于可读性(二进制文件一般都是乱码)

四、文件的打开和关闭

4.1 流和标准流

4.1.1 流

因为程序的数据需要和外部设备进行交互,而不同的外部设备交互方式不同,所以为了简化操作,抽象出一种概念——流。

流相当于数据与外部设备的中转站,程序员对流进行操作,就可以将数据与不同的外部设备交互,而并不需要知道其中的细节(相当于封装)。

4.1.2 标准流

那为什么我们从键盘输入数据,向屏幕上输出数据,并没有打开流呢?

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

  • stdin - 标准输入流,从键盘输入,scanf函数就是从标准输入流中读取数据。
  • stdout - 标准输出流,输出到屏幕,printf函数就是将信息输出到标准输出流中。
  • stderr - 标准错误流,输出到屏幕。

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

4.2 文件指针

文件类型指针,简称为文件指针。

  • 每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。
  • 这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE
//VS2013编译环境提供的 stdio.h 
struct _iobuf
{
	char *_ptr;
	int _cnt;
	char *_base;
	int _flag;
	int _file;
	int _charbuf;
	int _bufsiz;
	char *_tmpfname;
};
typedef struct _iobuf FILE;

那么,我们可以创建⼀个FILE*的指针变量pf,来维护FILE结构体(文件信息区),间接达到操作该文件的效果

4.3 fopen和fclose

在编写程序的时候,在打开文件的同时,都会返回⼀个FILE*的指针变量指向该文件,也相当于建立了指针和文件的关系

ANSIC 规定使用 fopen 函数来打开文件fclose关闭文件(可以类比于malloc和free的关系)

//打开⽂件
FILE * fopen ( const char * filename, const char * mode );
//关闭⽂件
int fclose ( FILE * stream );

打开文件时,要输入文件名和打开模式,如下代码:

int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}
	//操作文件
	//...
	fclose(pf);
	pf = NULL;
	return 0;
}

下面是一些常用的打开模式:

文件使用方式含义如果指定文件不存在
“r”(只读)为了输⼊数据,打开⼀个已经存在的⽂本⽂件出错
“w”(只写)为了输出数据,打开⼀个⽂本⽂件建⽴⼀个新的⽂件
“a”(追加)向⽂本⽂件尾添加数据建⽴⼀个新的⽂件

以上指令打开的是文本文件,如果要打开二进制文件,那就在后面加个b,比如rb,wb,ab。

五、文件的顺序读写

5.1 顺序读写函数

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

5.1.1 fgetc和fputc

int fputc ( int character, FILE* stream );

将字符写入流

int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}
	
	for (int i = 0; i < 26; ++i)
	{
		fputc('a' + i, pf);
	}

	fclose(pf);
	pf = NULL;
	return 0;
}
int fgetc ( FILE* stream );

从流中读取字符

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}

	for (int i = 0; i < 26; ++i)
	{
		printf("%c ", fgetc(pf));
	}

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

5.1.2 fgets和fputs

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

将字符串写入流

int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}
	
	for (int i = 0; i < 5; ++i)
	{
		fputs("hello world\n", pf);
	}

	fclose(pf);
	pf = NULL;
	return 0;
}
char* fgets ( char* str, int num, FILE* stream );

从流中读取num个字符(包含\0)放入字符数组

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}

	char arr[20] = "xxxxxxxxxxxxxxxxx";

	fgets(arr, 10, pf);

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

5.1.3 fscanf和fprintf

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

从流中读取格式化数据

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

将格式化数据写入流

typedef struct stu
{
	char name[20];
	int age;
	float score;
}stu;

int main()
{
	stu s = { 0 };
	fscanf(stdin, "%s %d %f", s.name, &(s.age), &(s.score));
	fprintf(stdout, "%s %d %f", s.name, s.age, s.score);
	return 0;
}

5.1.4 fread和fwrite

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

将ptr中count个大小为size的数据写入流

int main()
{
	stu s = { "lisi", 19, 69.36 };
	FILE* pf = fopen("data.txt", "wb");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}

	fwrite(&s, sizeof(s), 1, pf);

	fclose(pf);
	pf = NULL;
	return 0;
}
size_t fread ( void* ptr, size_t size, size_t count, FILE* stream );

从流中读取count个大小为size的数据到ptr

int main()
{
	stu s = { 0 };
	FILE* pf = fopen("data.txt", "rb");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}

	fread(&s, sizeof(s), 1, pf);

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

5.2 对比一组函数

函数名应用场景
scanf/printf针对标准输入/输出流
fscanf/fprintf针对所以输入/输出流
sscanf/sprintf针对字符串

六、文件的随机读写

6.1 fseek

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

可以根据偏移量和流起始位置,重定位文件指针

流起始位置有三种:

常量名位置
SEEK_SET文件开头
SEEK_CUR文件指针当前位置
SEEK_END文件结尾
int main()
{
	FILE* pf = fopen("data.txt", "r");

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

	fseek(pf, -3, SEEK_END);

	printf("%c", fgetc(pf));
	printf("%c", fgetc(pf));
	return 0;
}

6.2 ftell

long int ftell ( FILE * stream );

返回当前文件指针相对于文件开头的偏移量

6.3 rewind

void rewind ( FILE * stream );

让文件指针返回文件开头

七、文件读取结束的判定

7.1 是否结束的判定

文本文件读取是否结束:

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

二进制文件读取是否结束:

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

7.2 结束原因的判定

int feof ( FILE * stream );

判断是否遇到文件末尾(非0代表遇到)

int ferror ( FILE * stream );

判断是否遇到错误(非0代表遇到)


举个例子:

int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen fail");
		return 1;
	}

	int ch = 0;
	while ((ch = fgetc(pf)) != EOF)
	{
		putchar(ch);
	}

	if (feof(pf))
	{
		printf("reach the end of file");
	}
	else if (ferror(pf))
	{
		printf("I/O error while reading");
	}

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

八、文件缓冲区

其实,程序数据区和磁盘之间,并不是直接进行交互的,它们有一个中转站——文件缓冲区。无论是写入数据还是读取数据,都要等待缓冲区充满或者刷新,才能进行传递。

有了文件缓冲区,操作系统才能保持较高效率。要不然读取一个字符,就要打断一次操作系统,那效率将低的无法想象。

#include <stdio.h>
#include <windows.h>
//VS2019 WIN11环境测试
int main()
{
	FILE*pf = fopen("test.txt", "w");
	fputs("abcdef", pf);//先将代码放在输出缓冲区
	printf("睡眠10秒-已经写数据了,打开test.txt⽂件,发现⽂件没有内容\n");
	Sleep(10000);
	printf("刷新缓冲区\n");
	fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到⽂件(磁盘)
	//注:fflush 在⾼版本的VS上不能使⽤了
	printf("再睡眠10秒-此时,再次打开test.txt⽂件,⽂件有内容了\n");
	Sleep(10000);
	fclose(pf);
	//注:fclose在关闭⽂件的时候,也会刷新缓冲区
	pf = NULL;
	return 0;
}

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

看到这里了还不给博主扣个: ⛳️ 点赞☀️收藏 ⭐️ 关注! 💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要! 你们的点赞就是博主更新最大的动力! 有问题可以评论或者私信呢秒回哦。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录刷题第四十八天| 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

代码随想录刷题第四十八天 今天是打家劫舍三部曲&#xff0c;最后一题树形dp有点难&#xff0c;其他还好 打家劫舍 (LC 198) 题目思路&#xff1a; 代码实现&#xff1a; class Solution:def rob(self, nums: List[int]) -> int:dp [0 for _ in range(len(nums)1)]dp[1…

Open3D 截取感兴趣的点云部分

import time import open3d as o3d; import numpy as np; import matplotlib.pyplot as plt from scipy.signal import find_peaks#坐标 mesh_coord_frame o3d.geometry.TriangleMesh.create_coordinate_frame(size355, origin[0, 0, 0]) #mesh_coord_frame mesh_coord_frame…

机器学习_实战框架

文章目录 介绍机器学习的实战框架1.定义问题2.收集数据和预处理(1).收集数据(2).数据可视化(3).数据清洗(4).特征工程(5).构建特征集和标签集(6).拆分训练集、验证集和测试集。 3.选择算法并建立模型4.训练模型5.模型的评估和优化 介绍机器学习的实战框架 一个机器学习项目从开…

UVa1308/LA2572 Viva Confetti

题目链接 本题是2002年ICPC亚洲区域赛金沢(日本)赛区的H题 题意 我已经把n个圆盘依次放到了桌面上。现按照放置顺序依次给出各个圆盘的圆心位置和半径&#xff0c;问最后有多少圆盘可见&#xff1f;如下图所示。 分析 《训练指南》的题解&#xff1a; 题目说“保证在对输入数据…

87.乐理基础-记号篇-反复记号(一)反复、跳房子

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;86.乐理基础-记号篇-速度记号-CSDN博客 首先是反复记号表总结图&#xff1a; 当前是写前两个记号&#xff0c;其余记号后面写&#xff1a;这些反复记号最主要的目的很简单&#xff0c;还是为了节约纸张&#xff0c…

使用Linux安装Mysql Community Server 8.0.35

一、下载Mysql 官网&#xff1a;https://www.mysql.com/ 第一步&#xff1a;进入Linux官网&#xff0c;点击下载 第二步&#xff1a;点击MySQL Community (GPL) Downloads 第三步&#xff1a;进入页面&#xff0c;选择 MySQL Community Server 第四步&#xff1a;根据自己服务…

SpringBoot集成RabbitMq,RabbitMq消费与生产,消费失败重发机制,发送签收确认机制

RabbitMq消费与生产&#xff0c;消费失败重发机制&#xff0c;发送确认机制&#xff0c;消息发送结果回执 1. RabbitMq集成spring bootRabbitMq集成依赖RabbitMq配置RabbitMq生产者&#xff0c;队列&#xff0c;交换通道配置&#xff0c;消费者示例 2. RabbitMq消息确认机制消息…

LangChain 72 reference改变结果 字符串评估器String Evaluation

LangChain系列文章 LangChain 60 深入理解LangChain 表达式语言23 multiple chains链透传参数 LangChain Expression Language (LCEL)LangChain 61 深入理解LangChain 表达式语言24 multiple chains链透传参数 LangChain Expression Language (LCEL)LangChain 62 深入理解Lang…

Nginx——基础配置

和大多数软件一样&#xff0c;Nginx也有自己的配置文件&#xff0c;但它又有很多与众不同的地方&#xff0c;本帖就来揭开Nginx基础配置的面纱。 1、Nginx指令和指令块 了解指令和指令块有助于大家了解配置的上下文&#xff0c;下面是一个配置模板示例&#xff1a; 在这个配…

Transformer详解【学习笔记】

文章目录 1、Transformer绪论2、Encoders和Decoder2.1 Encoders2.1.1 输入部分2.1.2 多头注意力机制2.1.3 残差2.1.4 LayNorm&#xff08;Layer Normalization&#xff09;2.1.5 前馈神经网路 2.2 Decoder2.2.1 多头注意力机制2.2.2 交互层 1、Transformer绪论 Transformer在做…

第11章 GUI Page495~496 步骤三十一:另存为别的文件

当前的TrySaveFile(bool hint_on_dirty true)有两个特征无法满足“另存”的需求&#xff1a; 一&#xff0c;TrySaveFile仅在数据为“新”的时候才提问用户输入文件名。而“另存”总是要求用户输入一个文件名&#xff0c;多以它总应该弹出一个文件选择对话框&#xff0c;这也…

从零到一的方法:学习视频剪辑与嵌套合并技巧

随着社交媒体和数字技术的快速发展&#xff0c;视频制作已是常见的工作。那么如何批量嵌套合并视频呢&#xff1f;下面一起来看云炫AI智剪如何批量合并的方法。 嵌套合并后的视频截图查看。 批量嵌套合并的操作&#xff1a; 操作1、在云炫AI智剪上选择“嵌套合并”功能&#…

PHP版学校教务管理系统源码带文字安装教程

PHP版学校教务管理系统源码带文字安装教程 运行环境 服务器宝塔面板 PHP 7.0 Mysql 5.5及以上版本 Linux Centos7以上 系统介绍&#xff1a; 后台权限控制&#xff1a;支持多个管理员&#xff0c;学生管理&#xff0c;学生成绩&#xff0c;教师管理&#xff0c;文章管理&#x…

CSS3简单运用过渡元素(transition)

CSS3过渡 概念&#xff1a;在CSS3中&#xff0c;我们可以使用transition属性将元素的某一个属性从“一个属性值”在指定的时间内平滑地过渡到“另一个属性值”&#xff0c;从而实现动画效果。 CSS3变形&#xff08;transform)呈现的仅仅是一个结果&#xff0c;而CSS过渡&…

方波 离散傅里叶级数 MATLAB

%方波 离散时间傅里叶变换 L 5; N 10; k [-N/2:1:N/2]; %占空比 基本周期 离散时间的参数 xn [ones(1,L),zeros(1,N-L)]; %生成方波序列 XK dfs(xn,N); magXK abs([XK(N/21:N),XK(1:N/21)]); subplot(2,2,3); stem(k,magXK); axis([-N/2,N/2,-0.5,5.5]); xlabel(k); y…

麒麟OS + DM8数据库(Graalvm for JDK17) 测试

1、添加依赖 implementation com.dameng:DmJdbcDriver18:8.1.3.62 implementation com.baomidou:mybatis-plus-boot-starter:3.5.4 2、application.yml 数据源配置 spring: datasource: driver-class-name: dm.jdbc.driver.DmDriver #com.mysql.cj.jdbc.Driver url: jdbc:d…

Web前端 ---- 【Vue3】Proxy响应式原理

目录 前言 安装Vue3项目 安装 Proxy 语法格式 前言 从本文开始进入vue3的学习。本文介绍vue3中的响应式原理&#xff0c;相较于vue2中通过object.defineProperty&#xff08;vue2中的响应式&#xff09;来实现响应式&#xff0c;vue3中换成了Proxy来进行实现。 安装Vue3项目…

c#图片作为鼠标光标

图片转换为鼠标光标代码如下&#xff1a; private void Form1_Load(object sender, EventArgs e) {//button1.Cursor System.Windows.Forms.Cursors.Hand;Bitmap bmp new Bitmap("780.jpg");Cursor cursor new Cursor(bmp.GetHicon());button1.Cursor cursor;} …

腾讯云的域名使用阿里云服务器配置

因为近期云服务器到期了&#xff0c;之前的域名已经完成了备案不想轻易回收。于是就换了个厂商&#xff0c;从腾讯云换到了阿里云。但是因为两个厂商不互通。我又不想把域名转入到阿里云。所以就开启了配置之路&#xff0c;一路磕磕绊绊。给大家整理一份顺序&#xff0c;一步到…

在 Flutter 中创建圆角图像和圆形图像有多少种方法?

使用 Container 、 ClipRRect 、 CircleAvatar 、 Card 和 PhysicalModel 实现具有视觉吸引力的图像效果。 在 Flutter 应用 UI 设计中&#xff0c;圆形图像是常见的视觉元素。本博客探讨了使用不同技术实现圆形图像效果的各种方法。无论是使用网络图像、本地文件还是资源&…