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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章