C提高——配置文件案例分析

配置文件案例读写分析

/*
 *运行平台:Visual Studio 2015
 *参考资料:《C Primer plus 第六版》,传智扫地增C提高课程
*/


一、常用函数介绍

1、fgetc与fputc

这两个函数都是按字符的方式读写文件
fgetc

  • 函数原型:
int __cdecl fgetc( _Inout_ FILE* _Stream );
/* 参数说明
 - FILE* _Stream:为需要读取字符的文件指针
 */
  • 功能:从文件指针指定的文件读取一个字符
  • 返回值:成功:返回值为该字符的ASCII值
        失败:返回值为EOF,说明文件结束,值为-1。
  • 使用案例
	FILE *fp = NULL;	
	char *file_name = "d:/1.txt";
	char str[5] = {0};

	fp     = fopen(file_name, "r+");
	str[0] = fgetc(fp)

fputc

  • 函数原型:
int __cdecl fputc(  _In_  int   _Character,_Inout_ FILE* _Stream);
/* 参数说明
 - FILE* _Stream:为需要读取字符的文件指针
 - int   _Character:为输出的字符量。虽然函数被定义为整型数,但仅用其低八位
 */
  • 功能:将字符写到文件指针所指向的文件的当前写指针的位置

  • 返回值:成功:返回值为该字符的ASCII值,此时文件指针自动后移一个字节的位置
        失败返回值为EOF,说明写入失败,值为-1。

  • 使用案例

	FILE *fp = NULL;	
	char *file_name = "d:/1.txt";
	char str[5] = "1234";
	int ret;

	fp  = fopen(file_name, "r+");
	ret = fputc(str[1],fp);

2、fputs与fgets

这两个函数都是按行的方式读写文件
fgets

  • 函数原型:
char* __cdecl fgets(
    _Out_writes_z_(_MaxCount) char* _Buffer,	//指向字符数组的指针,该数组存储了要读取的字符串
    _In_                      int   _MaxCount,	//要读取的最大字符数(包括最后的空字符),通常是Buffer 的数组长度
    _Inout_                   FILE* _Stream		//读取文件的文件指针
    );
  • 功能:从指定的流中读取数据,每次读取一行,并把它存储在 Buffer 所指向的字符串内。

  • 返回值:成功:该函数返回相同的 Buffer 参数
         失败:如果到达文件末尾或者没有读取到任何字符,并返回一个空指针。如果发生错误,返回一个空指针

  • 使用案例

	FILE *fp = NULL;	
	char *file_name = "d:/1.txt";
	char str[5] = "1234";
	int ret;

	fp  = fopen(file_name, "r+");
	ret = fgets(str,sizeof(str),fp);

fputs

  • 函数原型:
int __cdecl fputs(
    _In_z_  char const* _Buffer,	//指向字符数组的指针,该数组存储了要写入的字符串
    _Inout_ FILE*       _Stream		//写入文件的文件指针
    );
  • 功能:从指定的文件写入一个字符串自动写入字符串结束标记符‘\0’)。

  • 返回值:成功:文件的位置指针会自动后移,函数返回值为非负整数
         失败:否则返回EOF,其值为-1。

  • 使用案例

	FILE *fp = NULL;	
	char *file_name = "d:/1.txt";
	char str[5] = "1234";
	int ret;

	fp  = fopen(file_name, "r+");
	ret = fputs(str,,fp);

3、fread与fwirte

这两个函数都是按块的方式读写文件
fread

  • 函数原型:
size_t __cdecl fread(
    _Out_writes_bytes_(_ElementSize * _ElementCount) void*  _Buffer,		//指向字符数组的指针,该数组存储了要读取的数据
    _In_                                             size_t _ElementSize,	//调用次数
    _In_                                             size_t _ElementCount,	//读取数据块的大小,一般为Buffer大小
    _Inout_                                          FILE*  _Stream			//读取文件的文件指针
  • 功能:给定文件Stream读取最多ElementCount个对象****到数组Buffer中(相当于以对每个对象调用size次fgetc)。
  • 返回值:成功:返回读取的对象个数,即ElementCount
         失败:返回值则可能小于count或为0
    fread不区分文件尾和错误,因此调用者必须用feof和ferror才能判断发生了什么。

fwrite

  • 函数原型:
size_t __cdecl fwrite(
    _In_reads_bytes_(_ElementSize * _ElementCount) void const* _Buffer,			//指向字符数组的指针,该数组存储了要写入的数据
    _In_                                           size_t      _ElementSize,	//调用次数
    _In_                                           size_t      _ElementCount,	//写入数据块的大小,一般为Buffer大小
    _Inout_                                        FILE*       _Stream			//写入文件的文件指针
    );
  • 功能:把Buffer所指向的大小为_ElementCoun的t数据块中的数据写入到给定流Stream中。
  • 返回值:成功:读取的对象个数,即ElementCount
         失败:返回值则可能小于count或为0
    fread不区分文件尾和错误,因此调用者必须用feof和ferror才能判断发生了什么。

二、配置文件案例

1、功能与设计

功能:提供一个测试菜单,实现对配置文件进行读写功能。
设计思路:实现这个功能,代码采用分层设计,
第一层:界面设计——编写测试菜单,直接调用读写的接口API函数。
第二层:底层设计——编写读写的接口API函数。

2、界面准备

测试菜单

void Menu(void)
{
	printf("\n---------------------\n");
	printf("0——Quit menu\n");
	printf("1——Write config file\n");
	printf("2——Read  config file\n");
	printf("---------------------\n");
	printf("Please enter your choice:");
}

int main()
{
	int choice;
	//	1、搭建测试框架
	for (;;)
	{
		Menu();
		scanf("%d", &choice);
		switch (choice)
		{
		case 0:
				exit(0);
			case 1:
				TWriteCfgItem();
				break;
			case 2:
				TGetCfgItem();
				break;
			default:
				exit(0);
		}
	}

	system("pause");
	return 0;
}

3、配置文件写

nt TGetCfgItem()
{
	char	name[1024] = { 0 };
	char	value[1024] = { 0 };
	int		valuelen = 0;
	int		ret;

	//	1、输入key
	printf("\nPlease enter key:"); 
	scanf("%s", name);

	//	2、执行函数
	ret = GetCfgItem(CFGFILE, name, value, &valuelen);

	//	3.1 查找失败
	if (ret != 0)
	{
		printf("Fun GetCfgItem() err:%d\n", ret);
		return ret;
	}

	//	3.2 查找成功
	printf("Value:%s", value);
	return 0;
}

/* 获取配置文件 */
int GetCfgItem(char *pFilename/*in*/, char *pKey/*in*/, char *pValue/*in out*/, int *pValuelen/*out*/)
{
	FILE *fp = NULL;
	char strline[Maxline] = { 0 };
	char *pTmp, *pStart, *pEnd;
	int flag, len,null_flag=0;
	
	if (pFilename == NULL || pKey == NULL || pValue == NULL | pValuelen == NULL)
	{
		return -1;
	}
	//	1、 打开文件,失败则返回
	fp = fopen(pFilename, "r");
	if (fp == NULL)
	{
		printf("File %s open fail\n", pFilename);
		return -2;
	}

	pTmp = pStart = pEnd = NULL;
	//	2、查找Key
	while (!feof(fp))
	{
		memset(strline, 0, sizeof(strline));

		//	2.1 从文件中读取数据存储到strline
		flag = fgets(strline, sizeof(strline), fp);

		if (flag == -1)
		{
			printf("File %s read fail\n", pFilename);
			return -3;
		}

		//	2.2 在strline数组中查找key
		pTmp = strstr(strline, pKey);
		pEnd = pTmp + strlen(strline);

		if (pTmp == NULL)
		{
			continue;
		}

		//	2.3 查找成功则
		pTmp = strchr(strline, '=');

		if (pTmp == NULL)
		{
			continue;
		}

		//	2.4 成功查找到Key后查找'='
		pTmp = pTmp + 1;
		pStart = pTmp;

		//	2.5 采用两头堵模型,去除可能存在的空格,只输出字符串
		while ((*pStart) == ' ')
		{
			pStart++;
		}
		while ((*pEnd) == ' ')
		{
			pEnd--;
		}

		/* 去除空格成功交换指针变量 */
		if (pStart < pEnd)
		{
			len = pEnd - pStart;
			pTmp = pStart;	
			null_flag = 1;
		}

		strncat(pValue, pTmp, len);	//存储value值到pValue指针指向内存空间中
		*pValuelen = len;			//记录其长度

		return 0;
	}

	if (null_flag == 0)
	{
		return -4;
	}
	return 0;
}

4、配置文件读

int TWriteCfgItem()
{
	char	name[1024] = { 0 };
	char	value[1024] = { 0 };
	int		valuelen = 0;
	int		ret;

	//	1、输入key
	printf("\nPlease enter key:");
	scanf("%s", name);

	//	2、输入value
	printf("\nPlease enter value:");
	scanf("%s", value);

	//	3、执行函数
	ret = WriteCfgItem(CFGFILE, name, value, &valuelen);

	//	4.1 写失败
	if (ret != 0)
	{
		printf("Fun GetCfgItem() err:%d\n", ret);
		return ret;
	}
	//	4.2 读成功则打印
	printf("Data :%s:%s\n", name,value);
	return 0;
}


/* 写配置文件 
 * 支持配置文件大小为:8K
 * 若输入的Key值在配置文件中已存在,则修改对应Value值
 * 若输入的Key值在配置文件中不存在,则在配置文件最后追加
 */
int WriteCfgItem(char *pFilename/*in*/, char *pKey/*in*/, char *pValue/*in*/, int *pValuelen/*in*/)
{
	FILE	*fp = NULL;
	char	strline[Maxline] = { 0 };
	char	fileline[1024 * 8] = { 0 };
	char	*pTmp;
	int		flag, length,null_flag=0;

	if (pFilename == NULL || pKey == NULL || pValue == NULL | pValuelen == NULL)
	{
		return -1;
	}

	//	1、打开文件,若不存在则新建
	fp = fopen(pFilename, "r+");
	if (fp == NULL)
	{
		printf("File %s open fail,we will new %s\n", pFilename, pFilename);
		fp = fopen(pFilename, "w+t");
		if (fp == NULL)
		{
			printf("New flie %s fail\n", pFilename);
			return -2;
		}
	}
	
	//	2、移动文件指针,计算文件长度
	fseek(fp, 0L, SEEK_END);
	length = ftell(fp);
	fseek(fp, 0L, SEEK_SET);

	if (length > 1024 * 8)
	{
		printf("File %s > 8K,fun WriteCfgItem() nonsupport\n", pFilename);
		return -3;
	}

	//	3、行扫描
	while (!feof(fp))
	{
		memset(strline, 0, sizeof(strline));
		//	3.1 读取行数据
		flag = fgets(strline, sizeof(strline), fp);

		if (flag == -1)
		{
			printf("File %s read fail\n", pFilename);
			return -4;
		}

		//	3.2 查找key
		pTmp = strstr(strline, pKey);

		if (pTmp == NULL)
		{
			strcat(fileline, strline);	//缓冲数据,进入下一行寻找
			continue;
		}
		else
		{
			//	3.3 查找到key,追加value
			sprintf(strline, "%s=%s\n", pKey, pValue);
			strcat(fileline, strline);	//缓冲数据
			null_flag = 1;
			continue;	//进入下一行,按行缓冲数据
		}
	}

	/* 全文件没有key,需追加 */
	if (null_flag == 0)
	{
		sprintf(strline, "%s=%s\n", pKey, pValue);
		strcat(fileline, strline);	//缓冲数据
	}

	/* 判断是否有换行符 */
	if ((fileline[strlen(fileline)-1]) != '\n')
	{
		strcat(fileline, "\n");
	}

	//	4、清空当前文件
	fp = fopen(pFilename, "w");
	
	//	5、把缓冲区的数据全部写入文件中
	fputs(fileline, fp);

	fclose(fp);
	fp = NULL;
	return 0;
}

三、完整编写文件

1、cfg_op.h文件

#ifdef  _CFG_OP_H
#define _CFG_OP_H

#ifdef  __cplusplus
extern "C" {
#endif

/* 获取配置文件 */
int GetCfgItem(char *pFilename/*in*/, char *pKey/*in*/, char *pValue/*in out*/, int *pValuelen/*out*/);
/* 写配置文件 */
int WriteCfgItem(char *pFilename/*in*/, char *pKey/*in*/, char *pValue/*in*/, int *pValuelen/*in*/);

#ifdef  __cplusplus
}
#endif

#endif // _CFG_OP_H

2、cfg_op.c文件

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

#define Maxline 2048
/* 获取配置文件 */
int GetCfgItem(char *pFilename/*in*/, char *pKey/*in*/, char *pValue/*in out*/, int *pValuelen/*out*/)
{
	FILE *fp = NULL;
	char strline[Maxline] = { 0 };
	char *pTmp, *pStart, *pEnd;
	int flag, len,null_flag=0;
	
	if (pFilename == NULL || pKey == NULL || pValue == NULL | pValuelen == NULL)
	{
		return -1;
	}
	//	1、 打开文件,失败则返回
	fp = fopen(pFilename, "r");
	if (fp == NULL)
	{
		printf("File %s open fail\n", pFilename);
		return -2;
	}

	pTmp = pStart = pEnd = NULL;
	//	2、查找Key
	while (!feof(fp))
	{
		memset(strline, 0, sizeof(strline));

		//	2.1 从文件中读取数据存储到strline
		flag = fgets(strline, sizeof(strline), fp);

		if (flag == -1)
		{
			printf("File %s read fail\n", pFilename);
			return -3;
		}

		//	2.2 在strline数组中查找key
		pTmp = strstr(strline, pKey);
		pEnd = pTmp + strlen(strline);

		if (pTmp == NULL)
		{
			continue;
		}

		//	2.3 查找成功则
		pTmp = strchr(strline, '=');

		if (pTmp == NULL)
		{
			continue;
		}

		//	2.4 成功查找到Key后查找'='
		pTmp = pTmp + 1;
		pStart = pTmp;

		//	2.5 采用两头堵模型,去除可能存在的空格,只输出字符串
		while ((*pStart) == ' ')
		{
			pStart++;
		}
		while ((*pEnd) == ' ')
		{
			pEnd--;
		}

		/* 去除空格成功交换指针变量 */
		if (pStart < pEnd)
		{
			len = pEnd - pStart;
			pTmp = pStart;	
			null_flag = 1;
		}

		strncat(pValue, pTmp, len);	//存储value值到pValue指针指向内存空间中
		*pValuelen = len;			//记录其长度

		return 0;
	}

	if (null_flag == 0)
	{
		return -4;
	}
	return 0;
}


/* 写配置文件 
 * 要求:
 * 所写配置文件最大为:8K
 * 
 */
int WriteCfgItem(char *pFilename/*in*/, char *pKey/*in*/, char *pValue/*in*/, int *pValuelen/*in*/)
{
	FILE	*fp = NULL;
	char	strline[Maxline] = { 0 };
	char	fileline[1024 * 8] = { 0 };
	char	*pTmp;
	int		flag, length,null_flag=0;

	if (pFilename == NULL || pKey == NULL || pValue == NULL | pValuelen == NULL)
	{
		return -1;
	}

	//	1、打开文件,若不存在则新建
	fp = fopen(pFilename, "r+");
	if (fp == NULL)
	{
		printf("File %s open fail,we will new %s\n", pFilename, pFilename);
		fp = fopen(pFilename, "w+t");
		if (fp == NULL)
		{
			printf("New flie %s fail\n", pFilename);
			return -2;
		}
	}
	
	//	2、移动文件指针,计算文件长度
	fseek(fp, 0L, SEEK_END);
	length = ftell(fp);
	fseek(fp, 0L, SEEK_SET);

	if (length > 1024 * 8)
	{
		printf("File %s > 8K,fun WriteCfgItem() nonsupport\n", pFilename);
		return -3;
	}

	//	3、行扫描
	while (!feof(fp))
	{
		memset(strline, 0, sizeof(strline));
		//	3.1 读取行数据
		flag = fgets(strline, sizeof(strline), fp);

		if (flag == -1)
		{
			printf("File %s read fail\n", pFilename);
			return -4;
		}

		//	3.2 查找key
		pTmp = strstr(strline, pKey);

		if (pTmp == NULL)
		{
			strcat(fileline, strline);	//缓冲数据,进入下一行寻找
			continue;
		}
		else
		{
			//	3.3 查找到key,追加value
			sprintf(strline, "%s=%s\n", pKey, pValue);
			strcat(fileline, strline);	//缓冲数据
			null_flag = 1;
			continue;	//进入下一行,按行缓冲数据
		}
	}

	/* 全文件没有key,需追加 */
	if (null_flag == 0)
	{
		sprintf(strline, "%s=%s\n", pKey, pValue);
		strcat(fileline, strline);	//缓冲数据
	}

	/* 判断是否有换行符 */
	if ((fileline[strlen(fileline)-1]) != '\n')
	{
		strcat(fileline, "\n");
	}

	//	4、清空当前文件
	fp = fopen(pFilename, "w");
	
	//	5、把缓冲区的数据全部写入文件中
	fputs(fileline, fp);

	fclose(fp);
	fp = NULL;
	return 0;
}

3、main.c文件

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

#define CFGFILE "d:/cfg.ini"

void Menu(void)
{
	printf("\n---------------------\n");
	printf("0——Quit menu\n");
	printf("1——Write config file\n");
	printf("2——Read  config file\n");
	printf("---------------------\n");
	printf("Please enter your choice:");
}

int TGetCfgItem()
{
	char	name[1024] = { 0 };
	char	value[1024] = { 0 };
	int		valuelen = 0;
	int		ret;

	//	1、输入key
	printf("\nPlease enter key:"); 
	scanf("%s", name);

	//	2、执行函数
	ret = GetCfgItem(CFGFILE, name, value, &valuelen);

	//	3.1 查找失败
	if (ret != 0)
	{
		printf("Fun GetCfgItem() err:%d\n", ret);
		return ret;
	}

	//	3.2 查找成功
	printf("Value:%s", value);
	return 0;
}

int TWriteCfgItem()
{
	char	name[1024] = { 0 };
	char	value[1024] = { 0 };
	int		valuelen = 0;
	int		ret;

	//	1、输入key
	printf("\nPlease enter key:");
	scanf("%s", name);

	//	2、输入value
	printf("\nPlease enter value:");
	scanf("%s", value);

	//	3、执行函数
	ret = WriteCfgItem(CFGFILE, name, value, &valuelen);

	//	4.1 写失败
	if (ret != 0)
	{
		printf("Fun GetCfgItem() err:%d\n", ret);
		return ret;
	}
	//	4.2 读成功则打印
	printf("Data :%s=%s\n", name,value);
	return 0;
}

int main()
{
	int choice;
	//	1、搭建测试框架
	for (;;)
	{
		Menu();
		scanf("%d", &choice);
		switch (choice)
		{
		case 0:
				exit(0);
			case 1:
				TWriteCfgItem();
				break;
			case 2:
				TGetCfgItem();
				break;
			default:
				exit(0);
		}
	}

	system("pause");
	return 0;
}

四、实际运行

在这里插入图片描述

发布了40 篇原创文章 · 获赞 29 · 访问量 3613

猜你喜欢

转载自blog.csdn.net/weixin_42813232/article/details/105152440