一、文件讀寫注意事項
注意事項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;
}