【C语言】文件操作强化

【C语言】文件操作强化



前言

本篇文章我们将详细讲到文件操作的相关函数使用和文件加密和解密的相关内容。


一、文件打开关闭

文件打开(fopen)

文件的打开操作表示将给用户指定的文件在内存分配一个FILE结构区,并将该结构的指针返回给用户程序,以后用户程序就可用此FILE指针来实现对指定文件的存取操作了。当使用打开函数时,必须给出文件名、文件操作方式(读、写或读写)。

FILE * fopen(const char * filename, const char * mode);
功能:打开文件
参数:
filename:需要打开的文件名,根据需要加上路径
mode:打开文件的权限设置
返回值:
成功:文件指针
失败:NULL
在这里插入图片描述

void test(){
	
	FILE *fp = NULL;

	// "\\"这样的路径形式,只能在windows使用
	// "/"这样的路径形式,windows和linux平台下都可用,建议使用这种
	// 路径可以是相对路径,也可是绝对路径
	fp = fopen("../test", "w");
	//fp = fopen("..\\test", "w");

	if (fp == NULL) //返回空,说明打开失败
	{
		//perror()是标准出错打印函数,能打印调用库函数出错原因
		perror("open");
		return -1;
	}
}
//应该检查fopen的返回值!如何函数失败,它会返回一个NULL值。如果程序不检查错误,这个NULL指针就会传给后续的I/O函数。它们将对这个指针执行间接访问,并将失败.


文件关闭(fclose)

文件操作完成后,如果程序没有结束,必须要用fclose()函数进行关闭,这是因为对打开的文件进行写入时,若文件缓冲区的空间未被写入的内容填满,这些内容不会写到打开的文件中。只有对打开的文件进行关闭操作时,停留在文件缓冲区的内容才能写到该文件中去,从而使文件完整。再者一旦关闭了文件,该文件对应的FILE结构将被释放,从而使关闭的文件得到保护,因为这时对该文件的存取操作将不会进行。文件的关闭也意味着释放了该文件的缓冲区。

int fclose(FILE * stream);
功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
参数:
stream:文件指针
返回值:
成功:0
失败:-1

它表示该函数将关闭FILE指针对应的文件,并返回一个整数值。若成功地关闭了文件,则返回一个0值,否则返回一个非0值.


二、文件读写函数

  • 按照字符读写文件:fgetc(), fputc()
  • 按照行读写文件:fputs(), fgets()
  • 按照块读写文件:fread(), fwirte()
  • 按照格式化读写文件:fprintf(), fscanf()
  • 按照随机位置读写文件:fseek(), ftell(), rewind()

字符读写函数

int fputc(int ch, FILE * stream);
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:
ch:需要写入文件的字符
stream:文件指针
返回值:
成功:成功写入文件的字符
失败:返回-1
int fgetc(FILE * stream);
功能:从stream指定的文件中读取一个字符
参数:
stream:文件指针
返回值:
成功:返回读取到的字符
失败:-1
int feof(FILE * stream);
功能:检测是否读取到了文件结尾
参数:
stream:文件指针
返回值:
非0值:已经到文件结尾
0:没有到文件结尾

//按照字符读写文件:fgetc(), fputc()
void test01()
{
	//写文件

	FILE* f_write = fopen("./test01.txt", "w+");

	if (f_write == NULL)
	{
		return;
	}

	char buf[] = "This is the first test";
	for (int i = 0; i < strlen(buf); i++)
	{
		fputc(buf[i], f_write);
	}
	fclose(f_write);


	//读文件

	FILE* f_read = fopen("./test01.txt", "r");	
	if (f_read == NULL)
	{
		return;
	}
	char ch;
	while ((ch = fgetc(f_read)) != EOF)	// EOF  Enf of File
	{
		printf("%c", ch);
	}
	fclose(f_read);
}

行读写函数

int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写入到stream指定的文件中, 字符串结束符 ‘\0’ 不写入文件。
参数:
str:字符串
stream:文件指针
返回值:
成功:0
失败:-1
char * fgets(char * str, int size, FILE * stream);
功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 ‘\0’ 作为字符串结束。
参数:
str:字符串
size:指定最大读取字符串的长度(size - 1)
stream:文件指针
返回值:
成功:成功读取的字符串
读到文件尾或出错: NULL

//按照行读写文件:fputs(), fgets()
void test02()
{
	//写文件
	FILE* f_write = fopen("./test02.txt", "w");
	if (f_write == NULL)
	{
		return;
	}
	char* buf[] =
	{
		"锄禾日当午\n",
		"汗滴禾下土\n",
		"谁知盘中餐\n",
		"粒粒皆辛苦\n",
	};
	for (int i = 0; i < 4; i++)
	{
		fputs(buf[i], f_write);
	}
	fclose(f_write);

	//读文件
	FILE* f_read = fopen("./test02.txt", "r");
	if (f_read == NULL)
	{
		return;
	}
	while (!feof(f_read))
	{
		char buf[1024] = { 0 };
		fgets(buf, 1024, f_read);
		printf("%s", buf);
	}
	fclose(f_read);
}

块读写函数

size_t fwrite(const void * ptr, size_t size, size_t nmemb, FILE * stream);
功能:以数据块的方式给文件写入内容
参数:
ptr:准备写入文件数据的地址
size: size_t 为 unsigned int类型,此参数指定写入文件内容的块数据大小
nmemb:写入文件的块数,写入文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:
成功:实际成功写入文件数据的块数,此值和nmemb相等
失败:0
size_t fread(void * ptr, size_t size, size_t nmemb, FILE * stream);
功能:以数据块的方式从文件中读取内容
参数:
ptr:存放读取出来数据的内存空间
size: size_t 为 unsigned int类型,此参数指定读取文件内容的块数据大小
nmemb:读取文件的块数,读取文件数据总大小为:size * nmemb
stream:已经打开的文件指针
返回值:
成功:实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明读到文件的结尾。
失败:0

//按照块读写文件:fread(),fwrite()
struct Hero
{
	char name[64];
	int age;
};
void test03()
{
	//写文件 wb二进制方式
	FILE* f_write = fopen("./test03.txt", "wb");
	if (f_write == NULL) 
	{
		return;
	}

	struct Hero heros[4] =
	{
		{ "亚瑟" , 18 },
		{ "赵云", 28 },
		{ "妲己", 19 },
		{ "孙悟空", 99 },
	};
	for (int i = 0; i < 4; i++)
	{
		//参数1 数据地址  参数2  块大小   参数3  块个数   参数4  文件指针
		fwrite(&heros[i], sizeof(struct Hero), 1, f_write);
	}
	fclose(f_write);

	//读文件
	FILE* f_read = fopen("./test03.txt", "rb");// read binary
	if (f_read == NULL) 
	{ 
		return;
	} 
	struct Hero temp[4];
	//参数1 数据地址  参数2  块大小   参数3  块个数   参数4  文件指针
	fread(&temp, sizeof(struct Hero), 4, f_read);

	for (int i = 0; i < 4; i++)
	{
		printf("姓名:%s 年龄:%d \n", temp[i].name, temp[i].age);
	}
	fclose(f_read); 
}

格式化读写函数

int fprintf(FILE * stream, const char * format, …);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 ‘\0’ 为止。
参数:
stream:已经打开的文件
format:字符串格式,用法和printf()一样
返回值:
成功:实际写入文件的字符个数
失败:-1
int fscanf(FILE * stream, const char * format, …);
功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:
stream:已经打开的文件
format:字符串格式,用法和scanf()一样
返回值:
成功:实际从文件中读取的字符个数
失败: - 1
注意:fscanf遇到空格和换行时结束。

//按照格式化读写文件:fprintf(), fscanf()
void test04()
{
	//写文件
	FILE* f_write = fopen("./test04.txt", "w");
	if (f_write == NULL)
	{
		return;
	}
	fprintf(f_write, "hello world %d年 %d月 %d日", 2024, 5, 30);
	//关闭文件
	fclose(f_write);

	//读文件
	FILE* f_read = fopen("./test04.txt", "r");
	if (f_read == NULL)
	{
		return;
	}
	char buf[1024] = { 0 };
	while(!feof(f_read)) 
	{
		fscanf(f_read, "%s", buf);
		printf("%s", buf);
	}
	fclose(f_read);
}

随机读写函数

int fseek(FILE * stream, long offset, int whence);
功能:移动文件流(文件光标)的读写位置。
参数:
stream:已经打开的文件指针
offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了 文件末尾,再次写入时将增大文件尺寸。
whence:其取值如下:
SEEK_SET:从文件开头移动offset个字节
SEEK_CUR:从当前位置移动offset个字节
SEEK_END:从文件末尾移动offset个字节
返回值:
成功:0
失败:-1
long ftell(FILE * stream);
功能:获取文件流(文件光标)的读写位置。
参数:
stream:已经打开的文件指针
返回值:
成功:当前文件流(文件光标)的读写位置
失败:-1
void rewind(FILE * stream);
功能:把文件流(文件光标)的读写位置移动到文件开头。
参数:
stream:已经打开的文件指针
返回值:
无返回值

//按照随机位置读写文件
void test05()
{
	FILE* f_write = fopen("./test05.txt", "wb");
	if (f_write == NULL) 
	{
		return;
	}
	struct Hero heros[4] = 
	{
		{ "亚瑟", 18 },
		{ "赵云", 28 },
		{ "妲己", 19 },
		{ "孙悟空", 99 },
	};
	for (int i = 0; i < 4; i++)
	{
		//参数1 数据地址  参数2  块大小   参数3  块个数   参数4  文件指针
		fwrite(&heros[i], sizeof(struct Hero), 1, f_write); 
	}
	fclose(f_write); 

	//读取妲己数据
	FILE* f_read = fopen("./test05.txt", "rb"); 
	if (f_read == NULL) 
	{
		//error 宏 

		//printf("文件打开失败\n");
		perror("文件打开失败"); 
		return;
	}
	//创建临时结构体
	struct Hero temp; 

	//改变文件光标位置
	fseek(f_read, sizeof(struct Hero) * 2, SEEK_SET);

	fseek(f_read, -(long)sizeof(struct Hero) * 2, SEEK_END);

	rewind(f_read); //将文件光标置首

	fread(&temp, sizeof(struct Hero), 1, f_read);

	printf("姓名: %s 年龄: %d\n", temp.name, temp.age);

	fclose(f_read); 
}

三、文件读写注意事项

//注意事项1
void test01()
{
	FILE* f_read = fopen("./test.txt", "r");
	if (f_read == NULL)
	{
		return;
	}

	char ch;

#if 0
	//aaaaaaaaaEOF
	//         |
	while (!feof(f_read))
	{
		ch = fgetc(f_read);

		if (feof(f_read))
		{
			break;
		}

		printf("%c", ch);
	}
#endif 

	while ((ch = fgetc(f_read)) != EOF)
	{
		printf("%c", ch);
	}

	fclose(f_read);
}

//注意事项2 
struct Hero
{
	char* name; //如果属性开辟到堆区,不要存指针到文件中,要将指针指向的内容存放到文件中
	int age;
};




int main() {
	test01();
	//printf(" aaaaa\n");
	//printf("%caaaaa\n",EOF);
	system("pause");
	return EXIT_SUCCESS;
}

四、配置文件读写案例

  • 1.文件中按照键值对方式 存放了有效的信息需要解析出来
    2.创建 config.h 和 config.c做配置文件读操作
    3.获取有效信息的行数 getFileLines
    4.判断字符串是否是有效行 int isValidLines(char *str)
    5.解析文件到配置信息数组中 void parseFile(char * filePath, int lines , struct ConfigInfo ** configinfo);
    6通过key获取value值 char * getInfoByKey(char * key, struct ConfigInfo * configinfo, int len);
    7释放内存 void freeConfigInfo(struct ConfigInfo * configinfo);
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "config.h"


int main(){

	char * filePath = "./config.txt";
	int len = getFileLines(filePath);
	printf("文件的有效行数为:%d\n", len);

	struct ConfigInfo * configInfo = NULL;
	parseFile(filePath, len, &configInfo);

	//测试根据key获取value
	printf("heroId = %s\n", getInfoByKey("heroId", configInfo, len));
	printf("heroName = %s\n", getInfoByKey("heroName", configInfo, len));
	printf("heroAtk = %s\n", getInfoByKey("heroAtk", configInfo, len));
	printf("heroDef = %s\n", getInfoByKey("heroDef", configInfo, len));
	printf("heroInfo = %s\n", getInfoByKey("heroInfo", configInfo, len));

	//释放空间
	freeConfigInfo(configInfo);
	configInfo = NULL;


	system("pause");
	return EXIT_SUCCESS;
}

config.c

#include "config.h"

int getFileLines(char * filePath)
{
	FILE * file = fopen(filePath, "r");
	if (file == NULL)
	{
		return -1;
	}

	char buf[1024] = { 0 };
	int lines = 0;
	while ( fgets(buf,1024,file) != NULL)
	{
		if (isValidLines(buf))
		{
			lines++;
		}
		memset(buf, 0, 1024);
	}

	return lines;

	fclose(file);
}

int isValidLines(char *str)
{
	if (strchr(str, ':') == NULL)
	{
		return 0; //返回假 代表无效行
	}

	return 1;
}

//解析文件
void parseFile(char * filePath, int lines, struct  ConfigInfo ** configinfo)
{
	struct ConfigInfo * info = malloc(sizeof(struct ConfigInfo) * lines);

	if (info == NULL)
	{
		return;
	}

	FILE * file = fopen(filePath, "r");
	if (file == NULL)
	{
		return;
	}
	char buf[1024] = { 0 };
	int index = 0;
	while ( fgets(buf,1024,file ) != NULL)
	{
		if (isValidLines(buf))
		{
			//有效信息 才去解析
			//清空 key和value数组
			//heroName:aaaa\n
			memset(info[index].key, 0, 64);
			memset(info[index].value, 0, 64);
			char * pos = strchr(buf, ':');
			strncpy(info[index].key, buf, pos - buf);
			strncpy(info[index].value, pos + 1, strlen(pos + 1)-1);
			index++;
		}
		memset(buf, 0, 1024);
	}
	*configinfo = info;
}

char * getInfoByKey(char * key, struct ConfigInfo * configinfo, int len)
{
	for (int i = 0; i < len;i++)
	{
		if (strcmp(key, configinfo[i].key) == 0)
		{
			return configinfo[i].value;
		}
	}

	return NULL;
}

//释放内存
void freeConfigInfo(struct ConfigInfo * configinfo)
{
	if (configinfo != NULL)
	{
		free(configinfo);
		configinfo = NULL;
	}
}

config.h

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//配置信息 结构体
struct ConfigInfo
{
	char key[64];
	char value[64];
};

//获取有效行数
int getFileLines(char* filePath);

//检测当前行是否是有效信息
int isValidLines(char* str);

//解析文件
void parseFile(char* filePath, int lines, struct  ConfigInfo** configinfo);

//根据key获取对应value
char* getInfoByKey(char* key, struct ConfigInfo* configinfo, int len);

//释放内存
void freeConfigInfo(struct ConfigInfo* configinfo);

总结

到这里这篇文章的内容就结束了,谢谢大家的观看,如果有好的建议可以留言喔,谢谢大家啦!

  • 87
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 56
    评论
评论 56
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值