文件处理的基本操作

news2024/11/25 14:57:15

一、文件与流

为什么要使用文件:程序的处理结果在程序结束后就消失,要想将程序运行的结果保存下来,就可以将相关内容保存在文件(file)中。

针对文件、键盘、显示器、打印机等外部设备的数据的读写操作都是通过流(stream)进行操作的。我们可以将流想象成流淌着字符的河流。

二、标准流 

stdin:标准输入流

用于读取普通输入的流。常用函数有scanf、getchar。

stdout:标准输出流

用于写入普通输出的流。常用函数有printf、putchar。

stderr:标准错误流

用于写出错误的流。通常是将错误信息显示到屏幕上。

三、FILE型

定义于<stdio.h>中,用于记录控制流所需要的信息,包含:

  1. 文件位置指示符:记录当前访问位置。
  2. 错误指示符:记录是否发生了错误的读取。
  3. 文件结束指示符:记录是否到达文件末尾。

FILE类型的指针变量指向的是文件信息区(结构体),包含文件名(由路径、文件名、文件类型组成)等信息。通过文件信息能够间接找到关联的文件。在不同的编译器中具体的实现方法是有差异的。

四、打开文件fopen

 打开文件的操作称为打开。库函数中的fopen函数用于打开文件。

//fopen函数的原型
FILE * fopen ( const char * filename, const char * mode );

使用该函数需要两个参数:第一个是需要的文件的文件名("文件名.文件类型"),第二个是打开该文件的方式。

fopen函数会为要打开的文件新建一个流。

如果打开成功,则返回一个FILE类型的指针,指向该文件的流 。否则返回一个空指针。

FILE* pf1 = fopen("test1.txt", "w");
//以只读的方式打开test1.txt,由于该文件之前不存在,所以会自动创建,文件在工程文件下
//如果以只写的方式打开一个不存在的文件会报错

//判断是否创建成功
if (pf == NULL)//创建失败返回值为null
{
	perror(fopen);
	return 1;
}

 文件的打开方式有:

(图源:明解C语言第三版)

需要注意的点:

(图源:明解C语言第三版)

 五、关闭文件fclose

关闭文件的操作称为关闭,库函数中的fclose函数用于关闭文件。

//fclose函数的原型
int fclose ( FILE * stream );

使用该函数需要的参数:指向想要关闭的文件的指针 。

如果关闭成功,则返回0。检查到错误时返回EOF(End-of-File,返回-1)。

//关闭文件
fclose(pf);
//在关闭文件后,可以将原先指向文件的指针置为空指针从而避免称为野指针
pf = NULL;

六、文件的顺序读写

6.1fputc

fputc:向文件中写入字符,适用于所有输出流

//函数原型
int fputc ( int character, FILE * stream );

 该函数的参数为一个int类型的字符(传递字符实际上就是传递字符的ASCII码值,所以可以设计为int类型)以及一个指向目标文件的流的指针。

int main()
{
	FILE* pf2 = fopen("test2.txt",  "w");
	char c;

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

	for (c = 'a'; c <= 'z'; c++)
	{
		fputc(c, pf2);
        //将字符c写入pf2指向的文件
	}

	fclose(pf2);
	pf2 = NULL;

	return 0;
}

6.2fgetc

fgetc:从文件中读取字符,适用于所有输入流

//函数原型
int fgetc ( FILE * stream );

该函数的参数只需要一个指向想要读取的文件的指针。

如果读取成功,返回读取到的字符的ASCII码值;

如果读取到文件末尾或者读取过程中遇到错误,返回(EOF)。

int main()
{
	FILE* pf2 = fopen("test2.txt", "r");

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

	int count = 0;
	int ch = 0;

	while ((ch=fgetc(pf2))!= EOF)
    //没有碰到文件末尾或者错误时,则继续循环
	{
		printf("%c", ch);
		//可以计算文件有多少个字符
		count++;
	}

	printf("count: %d\n", count);

	fclose(pf2);
	pf2 = NULL;

	return 0;
}

上面这段代码可以用来统计文件中有多少个字符。

6.3fputs

fputs:向文件中写入字符串

//函数原型
int fputs ( const char * str, FILE * stream );

第一个参数为指向想要写入的字符串的指针,第二个参数为指向目标文件的指针。 

 如果写入成功,则返回一个非负值。否则返回EOF并设置一个错误指示器。

 6.4fgets

fgets:从文件中读取字符串

//fgets函数的原型
char * fgets ( char * str, int num, FILE * stream );

该函数能够将目标文件中的内容拷贝到目标字符串中。 

第一个参数是一个指向要存放从文件中拷贝到的内容的字符串的指针,第二个参数是要拷贝的字符的个数,第三个参数是一个指向目标文件的指针。

如果读取正常,返回str指针。

如果遇到文件末尾或者读取过程中发生错误,返回null。

#include<string.h>
int main()
{
	FILE* pf3 = fopen("test3.txt", "r");

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

	char mystring[255];
	fgets(mystring, 255, pf3);//把pf3指向的内容复制到mystring
	puts(mystring);
	fclose(pf3);
	pf3 = NULL;

	return 0;
}

如果操作成功,则该函数返回要读取的内容。

如果在尝试读取字符时遇到文件末尾,则设置 eof 指示符 (feof)。如果在读取任何字符之前发生这种情况,则返回的指针为空指针(并且 str 的内容保持不变)。
如果发生读取错误,则设置错误指示符 (ferror),并返回 null 指针(但 str 指向的内容可能已更改)。

6.5fscanf

这是一个格式化输入函数,适用于所有输入流。

//fscanf函数的原型
int fscanf ( FILE * stream, const char * format, ... );
//scanf函数的原型
int scanf ( const char * format, ... );

通过对比观察我们可以发现:fscanf比scanf多了一个参数,即要读取的文件的指针

下面我们通过一个案例看看具体的使用过程

#include<stdio.h>

int main()
{
	int total = 0;//人数
	char name[100];
	double height, weight;
	double height_sum = 0;
	double weight_sum = 0;
	FILE* pf = fopen("data.dat", "r");

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

	while (fscanf(pf, "%s%lf%lf", name, &height, &weight) == 3)
	{
		printf("%-10s %5.1f %5.1f\n", name, height, weight);
		total++;
		height_sum += height;
		weight_sum += weight;
	}

	printf("--------------------------------\n");
	printf("average:    %5.1f %5.1f\n", height_sum / total, weight_sum / total);

	fclose(pf);
	pf = NULL;

	return 0;
}

我们来看看上面这段代码。

首先打开了要读取的文件"data.dat",再将指向该文件流的指针作为参数传递给fscanf函数获取相关数据,最后输出得到的数据。 

6.6fprintf

这是一个格式化输出函数,适用于所有输出流。

//fprintf函数的原型
int fprintf ( FILE * stream, const char * format, ... );
//print函数的原型
int printf ( const char * format, ... );

 通过对比观察我们可以发现,fprintf函数比printf函数多了一个参数,即指向要写入数据的文件的指针。

如果写入成功,则返回写入的字符个数。

如果写入错误,则设置错误指示符并返回负数。

如果写入宽字符时发生多字节字符编码错误,则 errno 设置为 EILSEQ 并返回负数。

6.7fread

二进制输入函数

//fread函数的原型
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

这个函数会从指定的文件流中读取数据,每次读取size*nmemb个字节,把它们存储到ptr指向的空间。

函数返回实际读取到的项数。

如果读取错误或读取到了文件末尾,则返回一个小于nmeneb的数。 

6.8fwrite

二进制输出函数

//fwrite函数原型
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

这个函数会从ptr指向的空间读取数据,每次读取size*count个字节,存入stream指向的文件流。

函数返回成功写入的字符的个数。

如果写入时出错(返回的数字和成功情况下的不同),在流中会设置一个错误指引(fereror)。

如果size或count为零,则返回零并设置错误指引。

下面是一个关于fwrite以及fread函数的简单使用案例:

#include<stdio.h>

enum
{
	SIZE = 5
};
int main()
{
	double a[SIZE] = { 1.,2.,3.,4.,5. };

	FILE* fp = fopen("test.bin", "wb");//必须使用二进制模式
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}

	fwrite(a, sizeof * a, SIZE, fp);//将数组a中的sizeof(a[0])*SIZE个字节的数据写入到fp指向的文件流
	fclose(fp);

	double b[SIZE];

	fp = fopen("test.bin", "rb");

	size_t ret_code = fread(b, sizeof * b, SIZE, fp);//将fp指向的文件流中的数据复制到b中,读取sizeof(b[0])*SIZE个字节
	
	if (ret_code == SIZE)
	{
		printf("read successfully\n");
		for (int i = 0; i < SIZE; i++)
		{
			printf("%lf ", b[i]);
		}
	}
	else
	{
		if (feof(fp))//feof用于判断读取结束的原因是否是因为遇到了文件末尾,如果到达了文件末尾返回非零值,否则返回零
		{
			printf("error reading test.bin:unexpected end of file\n");
		}
		else if (ferror(fp))//文件流中发生了错误,则返回1
		{
			printf("error reading test.bin\n");
		}
	}
	
	fclose(fp);
	fp = NULL;

	return 0;
}

首先定义了一个数组a并进行了初始化,然后再将目标文件打开并使用fwrite函数写入数据。

在使用fread函数时,通过判断返回值是否与数组大小相等再进行接下来的操作。 

七、文件的随机读写 

7.1fseek

该函数的作用是根据指针的偏移量来读取数据

//fseek函数原型
int fseek ( FILE * stream, long int offset, int origin );

第一个参数是目标文件流的指针,第二个参数指的是指针偏移量,第三个参数代表指针偏移的开始位置

int origin有三种参数:
 

图源:fseek - C++ Reference (cplusplus.com) 

第一个为文件的起始位置

第二个为当前指针的位置

第三个为文件的末尾

使用该函数的简单案例:

#include<stdio.h>

int main()
{
	FILE* pf = fopen("fseekTest.txt", "r");
	char ch;

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

	fseek(pf, 4, SEEK_CUR);//从当前指针(光标)向后偏移4
	ch = fgetc(pf);//得到e
	printf("%c\n", ch);

	fseek(pf, 2, SEEK_SET);//从起始位置开始
	ch = fgetc(pf);
	printf("%c\n", ch);

	fseek(pf, -3, SEEK_END);//从末尾开始
	ch = fgetc(pf);
	printf("%c\n", ch);

	fclose(pf);
	pf = NULL;

	return 0;
}

7.2ftell

该函数用于获取当前光标相对于起始位置的偏移量。

//ftell函数的原型
long int ftell ( FILE * stream );

该函数只需要一个参数:指向文件流的指针。

ftell使用的简单案例: 

int main()
{
	long int ptrPosition = 0;
	char ch;

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

	fseek(pf, 4, SEEK_CUR);//从当前指针(光标)向后偏移4
	ch = fgetc(pf);//得到e,读完之后光标向后偏移
	printf("%c\n", ch);

	ptrPosition=ftell(pf);//获取当前指针位置(光标\偏移量)

	printf("%d\n", ptrPosition);

	fclose(pf);
	pf = NULL;

	return 0;
}

7.3rewind

该函数用于将光标设置为起始位置 

//rewind函数的原型
void rewind ( FILE * stream );

该函数只需要一个参数:指向文件流的指针。

rewind函数使用的简单案例:

int main()
{
	char ch;
	long int pfPosition;

	FILE* pf = fopen("fseekTest.txt", "r");

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

	fseek(pf, 3, SEEK_SET);
	ch = fgetc(pf);
	printf("%c\n", ch);
	pfPosition = ftell(pf);
	printf("%d\n", pfPosition);
	rewind(pf);//将指针的位置更改为起始位置
	pfPosition = ftell(pf);
	printf("%d\n", pfPosition);

	fclose("pf");
	pf = NULL;

	return 0;
}

八、文件读取结束的判定 

判断文件结束的原因,我们可以用feof函数。

当我们打开文件流时,会有两个标记,一个是feof(用于判断是否到达文件末尾),另一个是ferror(用于判断文件读取过程是否发生错误)。

当文件读取结束的原因是遇到文件末尾(即EOF)说明读取正常。

如果在文件读取过程中发生错误,可以使用ferror函数来设置标记,表明文件读取结束不正常。

 完。

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

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

相关文章

阿里AI图片编辑新项目,人人都可做设计师。MimicBrush本地一键整合包下载

最近阿里巴巴联合香港大学开源了一个创新图像编辑工具&#xff1a;MimicBrush&#xff0c;这个工具相当于是一个局部重绘工具。它通过先进的AI技术&#xff0c;能够将一张图片的某一部分融合到另一张图片上。 MimicBrush&#xff0c;一款颠覆传统的图像编辑神器&#xff0c;不过…

在idea里使用github copilot ,就是这么好用

Github Copilot 代码补全等功能&#xff0c;提高写代码的效率 获取地址&#xff1a;https://web.52shizhan.cn/activity/copilot 如果之前是激活器激活的&#xff0c;请到环境变量里删除相关的copilot配置。 ① 发你注册的github账号的邮箱或用户名给客服&#xff0c;客服邀…

openppp2 控制台回显窗口输出内容详解

本文介绍 openppp2 的控制窗口回显内容&#xff0c;以LINUX平台为例子&#xff0c;其它平台大同小异&#xff0c;区别只是多了一些额外的输出选项内容&#xff0c;本文会在下面补充。 在本文开始之前请查阅关联内容&#xff1a;openppp2 命令行接口详解-CSDN博客 控制台客户端…

SpringEvent扩展性利器

使用Spring Event机制可以保证高扩展性&#xff1a; 使用Spring Event来发布应用内部领域事件&#xff0c;对于事件监听器可通过注解或类的方式来扩展&#xff0c;Spring Event内部使用观察者模式&#xff0c;但api使用层面可以完全解耦事件发布和事件监听&#xff1a; 常用方式…

建筑驱鸟设备 | 建筑专用超声波驱鸟器

从半夜的鸣叫到频繁的鸟粪污染&#xff0c;鸟类活动有时会成为城市居民不得不面对的小小困扰。通过合理的驱鸟方法&#xff0c;我们可以有效地减少鸟类对建筑物的侵扰&#xff0c;保护建筑物的完好和安全&#xff0c;同时维护城市居民的生活质量。 建筑专用超声波驱鸟器&#x…

spl序表字段批量修改

如果没有条件&#xff0c;全量修改,使用run 如果没有条件&#xff0c;是可以直接使用run函数&#xff0c;对指定一个列的所有都操作&#xff0c;但是没有任何条件 如果使用了筛选条件&#xff0c;使用了select函数&#xff0c;会返回一个被筛选并且修改的序表&#xff08;有条件…

ai assistant激活成功后,如何使用

ai assistant激活成功后&#xff0c;如图 ai assistant渠道&#xff1a;https://web.52shizhan.cn/activity/ai-assistant 在去年五月份的 Google I/O 2023 上&#xff0c;Google 为 Android Studio 推出了 Studio Bot 功能&#xff0c;使用了谷歌编码基础模型 Codey,Codey 是…

【贪心算法初级训练】在花坛上是否能种下n朵花、碰撞后剩余的行星

1、在花坛上是否能种下n多花 一个很长的花坛&#xff0c;一部分地已经种植了花&#xff0c;另一部分却没有&#xff0c;花不能种植在相邻的地块上否则它们会争夺水源&#xff0c;两者都会死去。给你一个整数数组表示花坛&#xff0c;由若干个0和1组成&#xff0c;0表示没种植花…

ThreadLocal的使用

ThreadLocal 一般不会单独使用&#xff0c;基本上都是 放在一个工具类中&#xff0c;然后在拦截器中去使用&#xff08;至少存储 Token 的时候是这样的&#xff09; 这里为了更加还原真实的线上环境&#xff0c;直接就用了 拦截器统一返回全局异常捕获ThreadLocal&#xff0c;…

生信算法8 - HGVS转换与氨基酸字母表

HGVS 概念 HGVS 人类基因组变异协会(Human Genome Variation Society)提出的转录本编号&#xff0c;cDNA 参考序列(以前缀“c.”表示)、氨基酸参考序列(以前缀“p.”表示)。cDNA 中一种碱基被另一种碱基取代&#xff0c;以“>”进行表示&#xff0c;如&#xff1a;c.2186A&…

软件测试之解构单元测试

软件单元测试是对软件中的最小可测试单元进行检查和验证的过程。这些单元可以是函数、方法、类实例&#xff0c;或者是任何具有明确功能、规格定义和接口定义的程序代码模块。单元测试是软件开发过程中的最低级别的测试活动&#xff0c;它确保软件的独立单元在与程序的其他部分…

Jar打包成Service在Window运行

直接启动 bat脚本直接启动jar包 Spring Boot 部署在Windows Service启动 环境 电脑环境需要安装Microsoft.NETFramework 4 Microsoft.NETFramework 4 打包 WinSW 下载exe和xml配置文件&#xff0c;将exe和xml放在同一个目录下&#xff0c;并且重命名为一样的名字&…

Ideogram-免费使用的 AI 工具,可以生成逼真的图像、海报、徽标

工具来源:Ideogram | AI工具箱 什么是Ideogram AI? Ideogram AI是一款高效的工具,旨在将文本与AI生成的图像结合在一起。该应用程序提供了用户友好的界面,使您能够轻松地制作出色的艺术作品、标志和设计。 与传统工具不同,Ideogram AI因其能够以无与伦比的简便和速度将…

Git 查看当前分支是基于哪个分支拉取(源头分支)

场景&#xff1a; 项目中使用 Git 管理代码仓库的时候&#xff0c;随着项目的持续迭代及项目的扩展&#xff0c;多版本并行开发是非常常见的事情&#xff0c;多版本并行开发就伴随着多分支&#xff0c;随着 Git 的分支越拉越多&#xff0c;这时候很容易造成分支的混乱&#xf…

webgis 之 地图投影

地图投影 什么是地图投影目的种类等角投影的分类墨卡托投影Web 墨卡托投影 参考小结 为了更好地展示地球上的数据&#xff0c;需要将地球投影到一个平面上。地图投影是一个数学问题&#xff0c;按照一定的几何关系&#xff0c;将地球上的经纬度坐标映射到一个平面上的坐标。地球…

由 Vault 支持的 KES 的 MinIO Operator

为了提供安全锁定和擦除的合规性功能&#xff0c;MinIO 使用服务器端加密 &#xff08;SSE&#xff09; 在存储层加密对象&#xff0c;以保护对象作为写入操作的一部分。MinIO 以极高的效率做到这一点——基准测试表明 MinIO 能够以接近线速进行加密/解密。 MinIO 使用的秘诀是…

【头歌】HBase扫描与过滤答案 解除复制粘贴限制

解除复制粘贴限制 当作者遇到这个限制的时候火气起来了三分&#xff0c;然后去网上搜索答案&#xff0c;然后发现了一位【碳烤小肥肠】居然不贴代码&#xff0c;XX链接&#xff0c;贴截图&#xff0c;瞬时火气冲顶&#xff0c;怒写此文 首先启动万能的控制台&#xff0c;然后C…

帕金森综合征的预防方法

帕金森综合征是一种慢性神经退行性疾病&#xff0c;目前尚无法彻底治愈。然而&#xff0c;通过采取一些预防措施&#xff0c;可以降低患病风险或延缓病情发展。以下是一些基于最新研究和医学建议的预防方法&#xff1a; 健康饮食&#xff1a;保持低盐、低脂饮食&#xff0c;多吃…

SQL新手蜕变:掌握这20条常用SQL语句,让你也能成为高手!

序言 在现代软件开发中&#xff0c;SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;作为与数据库交互的标准编程语言&#xff0c;是每个开发者必学的基础技能。掌握SQL并在数据库管理与数据分析中应用自如&#xff0c;能显著提升开发效率和数…

语音相关算法学习整理

最近看了一下百度paddlespeech的一些公开课&#xff0c;把课程里的视频内容大体听了一下&#xff0c;现在整理一下笔记。教程链接见&#xff1a;飞桨AI Studio星河社区-人工智能学习与实训社区 语音识别的过程可以这样简单概括&#xff1a; 将声音信号经过预加重、加窗、fft等…