C语言详解文件操作(二):文件读写注意事项、配置文件读写、文件加密与解密

一、文件读写注意事项

注意事项1:feof产生的滞后性问题(按照字符方式读取时出现的问题):feof有滞后性,会多读一个EOF(文件结尾标志),导致输出结果多一个空格一样的东西。
先来看如下一个例子,我们手动添加一个text.txt文档输入aaaaaa,对其读取。

void test()
{
	FILE* file = fopen("./test.txt","r");
	if (file == NULL)
	{
		return;
	}
	char ch;
	while (!feof(file))//feof有滞后性,会多读一个EOF(文件结尾标志)
	{
		ch = fgetc(file);
		printf("%c",ch);
	}
	fclose(file);
}

输出结果:虽然打印成功了,但是多打印出来一段空格一样的东西。
在这里插入图片描述
那么这个东西是什么呢?它就是文件尾部EOF,我们来看一下,单独输出这一行:

printf("%c",EOF);

打印结果:这就是输出的样子,一个像空格一样的东西。
在这里插入图片描述
那么如何去解决这个问题呢?
我们只需要再判断一下文件是否都到末尾,若读到就break退出循环即可,不用再执行下一次了。

void test()
{
	FILE* file = fopen("./test.txt","r");
	if (file == NULL)
	{
		return;
	}
	char ch;
	while (!feof(file))
	{
		ch = fgetc(file);
		if (feof(file))
		{
			break;//当flag为真时,退出循环。
		}
		printf("%c",ch);
	}
	fclose(file);
}

输出结果:没有产生EOF,我们也可以使用另外一种判断方式也不会产生问题。

void test()
{
	FILE* file = fopen("./test.txt","r");
	if (file == NULL)
	{
		return;
	}
	char ch;
	while ((ch=fgetc(file)) != EOF)//这种方式不会产生多一个EOF的问题
	{
		printf("%c",ch);
	}
	fclose(file);
}

修改后的方式与新的方式产生同样的输出结果:无EOF产生。
在这里插入图片描述
注意事项2:开辟在堆区的指针,不要把指针写入文件中,而要把指针指向的内容写入文件: 因为读取指针是读取它的地址,假设它为0x01,再读取出来还是0x01没有意义,而要写入的是指针指向的内容。

struct Person
{
	char* name;//不要将指针写入文件,而要将指针指向的内容写入文件
	int age;
};

 

二、配置文件读写

我们直接来看一个案例:
需求分析: 假如我们现在在当前项目的目录下已经有一个配置文件config.txt,文件中写入如下内容:
在这里插入图片描述
①#的内容都是注释的东西,不需要做任何解析,例如第一行为英雄的Id,下一行即为具体的数据。
②数据存放方式用冒号分割,冒号前为属性索引值,后面值为实值,为一个键——值对,键为索引,值为有效数据。带冒号的数据为有效数据(键值对出现的为有效数据),其他均为无效数据。
③我们需要将有效数据解析成功。解析成功放入键值对中,键值对为结构体struct ConfigInfo;heroId放入key中,1放入value中,后面同理。需要用一个结构体数组维护这里面所有的数据,最后读取出来。
在这里插入图片描述
下面我们再创建2个文件,加上我么的源文件总三个文件。
头文件config.h :存放结构体以函数声明
config.c :实现函数
main文件:调用接口进行测试

实现代码:
config.cpp

#include"config.h"
//获取有效函数
int getFileLine(char* fileName)
{
	FILE* file = fopen(fileName,"r");
	if (file == NULL) return -1;
	
	char buf[1024] = {0};
	int lines = 0;
	while (fgets(buf,1024,file) != NULL)
	{
		if(isValidLine(buf))//有效行,才统计它
		{
			lines++;
		}
	}
	fclose(file);

	return lines;
}

//判断当前行是否有效
int isValidLine(char* str)
{
	if (str[0] == ' ' || str[0]=='\0' || strchr(str,':')==NULL)//无效数据
	{
		return 0;//无效数据都返回假
	}
	return 1;
}

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

	FILE* file = fopen(filePath,"r");
	char buf[1024] = {0};
	int index = 0;
	while (fgets(buf,1024,file) != NULL)
	{
		if (isValidLine(buf))//解析有效数据
		{
			memset(info[index].key,0,64);
			memset(info[index].value,0,64);

			char* pos = strchr(buf,':');//pos代表冒号的位置
			strncpy(info[index].key,buf,pos-buf);//将key截取到结构体中
			strncpy(info[index].value,pos+1,strlen(pos+1));//将value截取到结构体中
			index++;
		}
		memset(buf,0,1024);
	}

	*configInfo = info;
}

//根据索引值来获取实值
char* getInfoByKey(char* key,struct ConfigInfo* configInfo,int line)
{
	for (int i=0; i<line; i++)
	{
		if (strcmp(key,configInfo[i].key) == 0)
		{
			return configInfo[i].value;
		}
	}
	return NULL;
}

//释放信息
void freeSpace(struct ConfigInfo* configInfo)
{
	if (configInfo != NULL)
	{
		free(configInfo);
		configInfo = NULL;
	}
}

config.h

//Author:Mr.Rain
//Data:2020.2.13
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct ConfigInfo
{
	char key[64];//索引值
	char value[64];//实值
};

//获取有效函数
int getFileLine(char* fileName);

//判断当前行是否有效
int isValidLine(char* str);

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

//根据索引值来获取实值
char* getInfoByKey(char* key,struct ConfigInfo* configInfo,int line);

//释放信息
void freeSpace(struct ConfigInfo* configInfo);

主函数中的测试函数:

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

	struct ConfigInfo* pArray = NULL;
	parseFile(filePath,line,&pArray);

	//测试根据p访问value
	printf("heroId = %s\n",getInfoByKey("heroId",pArray,line));
	printf("heroName = %s\n",getInfoByKey("heroName",pArray,line));
	printf("heroAtk = %s\n",getInfoByKey("heroAtk",pArray,line));
	printf("heroDef = %s\n",getInfoByKey("heroDef",pArray,line));
	printf("heroInfo = %s\n",getInfoByKey("heroInfo",pArray,line));
}

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

三、文件加密与解密

例如上面的例子,我们想对配置文件进行加密解密,下面为加密与解密过程:
在这里插入图片描述
文件加密:

//文件加密
void codeFile(char* sourceFile,char* destFile)
{
	FILE* fp1 = fopen(sourceFile,"r");
	FILE* fp2 = fopen(destFile,"w");
	if (!fp1 || !fp2) return;

	char ch;
	while ((ch = fgetc(fp1)) != EOF)
	{
		//#35 0000 0000 0010 0011  << 4 
		//0000 0010 0011 0000
		short temp = (short)ch;
		temp = temp <<4;

		//1000 0000 0000 0000 | 0000 0010 0011 0000
		//1000 0010 0011 0000
		temp = temp | 0x8000;

		//1000 0010 0011 0000在加一个0-15之间的随机数
		temp = temp + rand()%16;

		//加密后数据写入到文件中
		fprintf(fp2,"%hd",temp);
	}

	fclose(fp1);
	fclose(fp2);
}

加密结果:加密成功
在这里插入图片描述
文件解密:

//文件解密
void deCodeFile(char* sourceFile,char* destFile)
{
	FILE* fp1 = fopen(sourceFile,"r");
	FILE* fp2 = fopen(destFile,"w");
	if (!fp1 || !fp2) return;

	short temp;
	while (!feof(fp1))
	{
		fscanf(fp1,"%hd",&temp);
		//1000 0010 0011 1010 << 1先左移一位
		temp = temp<<1;
		//000 0010 0011 10100 >> 再右移动5位
		temp = temp>>5;
		//0000 0000 0010 0011
		char ch = (char)temp;

		fputc(ch,fp2);
	}
	fclose(fp1);
	fclose(fp2);
}

解密结果:解密成功。
在这里插入图片描述

四、源代码

上述完整源代码如下:
config.h

//Author:Mr.Rain
//Data:2020.2.19
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct ConfigInfo
{
	char key[64];//索引值
	char value[64];//实值
};

//获取有效函数
int getFileLine(char* fileName);

//判断当前行是否有效
int isValidLine(char* str);

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

//根据索引值来获取实值
char* getInfoByKey(char* key,struct ConfigInfo* configInfo,int line);

//释放信息
void freeSpace(struct ConfigInfo* configInfo);

config.cpp(由于VS2012的.c文件在结构控制语句时若不提前声明会报错,十分不方便,这里统一使用.cpp文件)

//Author:Mr.Rain
//Data:2020.2.19
#include"config.h"
//获取有效函数
int getFileLine(char* fileName)
{
	FILE* file = fopen(fileName,"r");
	if (file == NULL) return -1;
	
	char buf[1024] = {0};
	int lines = 0;
	while (fgets(buf,1024,file) != NULL)
	{
		if(isValidLine(buf))//有效行,才统计它
		{
			lines++;
		}
	}
	fclose(file);

	return lines;
}

//判断当前行是否有效
int isValidLine(char* str)
{
	if (str[0] == ' ' || str[0]=='\0' || strchr(str,':')==NULL)//无效数据
	{
		return 0;//无效数据都返回假
	}
	return 1;
}

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

	FILE* file = fopen(filePath,"r");
	char buf[1024] = {0};
	int index = 0;
	while (fgets(buf,1024,file) != NULL)
	{
		if (isValidLine(buf))//解析有效数据
		{
			memset(info[index].key,0,64);
			memset(info[index].value,0,64);

			char* pos = strchr(buf,':');//pos代表冒号的位置
			strncpy(info[index].key,buf,pos-buf);//将key截取到结构体中
			strncpy(info[index].value,pos+1,strlen(pos+1));//将value截取到结构体中
			
			index++;
		}
		memset(buf,0,1024);
	}

	*configInfo = info;
}

//根据索引值来获取实值
char* getInfoByKey(char* key,struct ConfigInfo* configInfo,int line)
{
	for (int i=0; i<line; i++)
	{
		if (strcmp(key,configInfo[i].key) == 0)
		{
			return configInfo[i].value;
		}
	}
	return NULL;
}

//释放信息
void freeSpace(struct ConfigInfo* configInfo)
{
	if (configInfo != NULL)
	{
		free(configInfo);
		configInfo = NULL;
	}
}

code.h

//Author:Mr.Rain
//Data:2020.2.19
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//文件加密
void codeFile(char* sourceFile,char* destFile);


//文件解密
void deCodeFile(char* sourceFile,char* destFile);

code.cpp

//Author:Mr.Rain
//Data:2020.2.19
#include "code.h"
#pragma once

//文件加密
void codeFile(char* sourceFile,char* destFile)
{
	FILE* fp1 = fopen(sourceFile,"r");
	FILE* fp2 = fopen(destFile,"w");
	if (!fp1 || !fp2) return;

	char ch;
	while ((ch = fgetc(fp1)) != EOF)
	{
		//#35 0000 0000 0010 0011  << 4 
		//0000 0010 0011 0000
		short temp = (short)ch;
		temp = temp <<4;

		//1000 0000 0000 0000 | 0000 0010 0011 0000
		//1000 0010 0011 0000
		temp = temp | 0x8000;

		//1000 0010 0011 0000在加一个0-15之间的随机数
		temp = temp + rand()%16;

		//加密后数据写入到文件中
		fprintf(fp2,"%hd",temp);
	}

	fclose(fp1);
	fclose(fp2);
}

//文件解密
void deCodeFile(char* sourceFile,char* destFile)
{
	FILE* fp1 = fopen(sourceFile,"r");
	FILE* fp2 = fopen(destFile,"w");
	if (!fp1 || !fp2) return;

	short temp;
	while (!feof(fp1))
	{
		fscanf(fp1,"%hd",&temp);
		//1000 0010 0011 1010 << 1先左移一位
		temp = temp<<1;
		//000 0010 0011 10100 >> 再右移动5位
		temp = temp>>5;
		//0000 0000 0010 0011
		char ch = (char)temp;

		fputc(ch,fp2);
	}
	fclose(fp1);
	fclose(fp2);
}

main.cpp

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "code.h"

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

	struct ConfigInfo* pArray = NULL;
	parseFile(filePath,line,&pArray);

	//测试根据p访问value
	printf("heroId = %s\n",getInfoByKey("heroId",pArray,line));
	printf("heroName = %s\n",getInfoByKey("heroName",pArray,line));
	printf("heroAtk = %s\n",getInfoByKey("heroAtk",pArray,line));
	printf("heroDef = %s\n",getInfoByKey("heroDef",pArray,line));
	printf("heroInfo = %s\n",getInfoByKey("heroInfo",pArray,line));
}

void test2()
{
	codeFile("./config.txt","./加密文件.txt");
	deCodeFile("./加密文件.txt","./解密文件.txt");
}

int main()
{
	test2();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值