一、文件读写注意事项
注意事项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;
}