您的位置:首页 > 编程语言 > C语言/C++

【文件】配置文件读写

2014-08-17 14:20 169 查看

配置文件接口设计思路

配置文件后缀名为“ini”
其图标一般如下:



1.写配置文件
WriteCfgFile(FILE *fp, char *key, char val);
//如果定义函数接口需要在此函数外部就要打开文件了,这给调用该函数的人带来一定麻烦,这就属于模块划分不合理了。
//所以一般情况下是在此文件中打开文件,而调用该函数的人只需要告诉该函数需要打开的那个文件名即可。
//所以将此函数改进成如下。
WriteCfgFile(char *fname, char *key, char val);
//这里Master好像是说:万一fname所指文件中含有不可见字符,则...(啥玩意儿,没听清)...,所以要对上述函数再做些改进,如下所示。
WriteCfgFile(char *fname, char *key, int keylen, char val, int val_len);
 
2.读配置文件与写配置文件类似,这里就不多说了。
 

配置文件读写操作

直接看下面的例子

config.h

#ifndef _CONFIG_H_
#define _CONFIG_H_

int GetCfgItem(char *pFileName, char *pKey, char *pValue, int *pValueLen);
int SetCfgItem(char *pFileName, char *pKey, char *pValue, int *pValueLen);
int WriteCfgItem(char *pFileName, char *pItemName, char *pItemValue, int itemValueLen);

#endif
 

config.c

#include "config.h"
#include "stdio.h"
#include "string.h"

#define LineMaxLen 2048
#define KeyMaxLen 64

//实现流程
//1. 打开文件;
//2. 循环读取读取文件每一行;
//3. 解析每一行,若匹配key关键字,再进行value值得提取
//4. 提取value值时需要去除前后空格(级指针典型应用)
int GetCfgItem(char *pFileName, char *pKey, char *pValue, int *pValueLen)
{
int rv = 0;
FILE *fp = NULL;
char lineBuf[LineMaxLen];
char *pTmp = NULL, *pBegin = NULL, *pEnd = NULL;

if(pFileName == NULL || pKey == NULL || pValue == NULL || pValueLen == NULL)
{
rv = -1;
printf("GetCfgItem() err: %d.\n", rv);
goto End;
}

fp = fopen(pFileName, "r");
if(fp == NULL)
{
rv = -2;
printf("GetCfgItem() call fopen() err: %d.\n", rv);
goto End;
}
while(!feof(fp))
{
//读每一行
memset(lineBuf, 0, sizeof(lineBuf));
pTmp = fgets(lineBuf, LineMaxLen, fp);
if(pTmp == NULL)
{break;}

//若不含=号,则为非配置项
pTmp = strchr(lineBuf, '=');//函数返回一个指向str 中ch 首次出现的位置,当没有在str 中找ch到返回NULL。
if(pTmp == NULL)
{continue;}

//检查key是否在本行
pTmp = strstr(lineBuf, pKey);
if(pTmp == NULL)
{continue;}

//调整到=右边,为获取value做准备
pTmp = strchr(lineBuf, '=');
if(pTmp == NULL)
{continue;}
pTmp = pTmp + 1;

//获取value起点
while(1)
{
if(*pTmp == ' ')
{
pTmp++;
}
else
{
pBegin = pTmp;
if(*pBegin == '\n')
{
//没有配置value
printf("配置项:%s 没有配置value \n", pKey);
goto End;
}
break;
}
}

//获取value结束点
while(1)
{
if(*pTmp == ' ' || *pTmp == '\n')
{break;}
else
{pTmp++;}
}
pEnd = pTmp;

//赋值
*pValueLen = pEnd - pBegin;
memcpy(pValue, pBegin, pEnd-pBegin);//函数从pBegin中复制pEnd-pBegin 个字符到pValue中,并返回pValue指针。如果pValue 和pBegin 重叠,则函数行为不确定。
break;
}

End:
if(fp != NULL)
{
fclose(fp);
}
return rv;
}

//实现流程
//循环读取文件每一行,检查key关键字是否存在,若存在则修改对应的value值;若不存在,则在文件末尾添加“key = value”。
//难点:如何修改文件流中的值
int SetCfgItem(char *pFileName, char *pKey, char *pValue, int *pValueLen)
{
//参数pValueLen在本函数中没有被用到。
int rv = 0, iTag = 0, length = 0;
FILE *fp = NULL;
char linebuf[LineMaxLen];
char *pTmp = NULL, *pBegin = NULL, *pEnd = NULL;
char filebuf[1024*8] = {0};

if(pFileName == NULL || pKey == NULL || pValue == NULL)
{
rv = -1;
printf("func SetCfgItem() err: %d.\n",rv);
goto End;
}

fp = fopen(pFileName, "r+");//以读写方式打开一个已存在的字符文件
if(fp == NULL)
{
rv = -2;
printf("func SetCfgItem() call fopen() err: \n",rv);
//goto End;
}
if(fp == NULL) //如果文件打开失败,则新建一个文件。
{
fp = fopen(pFileName, "w+t");//打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
if(fp == NULL)
{
rv = -3;
printf("func SetCfgItem() call fopen() err: \n",rv);
goto End;
}
}

fseek(fp, 0L, SEEK_END); //把文件指针从文件尾向文件头方向移动个字节,即将文件指针移到文件尾。(文件尾:联想一篇文章的末尾;文件头:联想一篇文章的开头)
length = ftell(fp); //获取文件长度
fseek(fp, 0L, SEEK_SET); //把文件指针从文件头向文件尾方向移动个字节,即将文件指针移到文件头。

if(length > 1024*8)
{
rv = -3;
printf("文件超过*8,nunsupport.");
goto End;
}
while(!feof(fp))//判断是否到达文件尾
{
//读每一行
memset(linebuf, 0, sizeof(linebuf));//将lineBuf清为
pTmp = fgets(linebuf, LineMaxLen, fp);//读取fp文件中当前行字符串到linebuf中,并返回这个字符串的首地址。
if(pTmp == NULL)//若fgets()读到文件尾或出错,则返回NULL
{break;}

//pKey关键字是否在本行
pTmp = strstr(linebuf, pKey);//找出linebuf在pKey中首次出现的位置,并返回其地址。
if(pTmp == NULL)//如果没有找到则返回NULL
{
strcat(filebuf, linebuf);//将linebuf连接到filebuf中。
//continue;//多余的
}
else//如果找到了linebuf在pKey中首次出现的位置
{
sprintf(linebuf, "%s = %s\n", pKey, pValue);//如果找到了,则不管pValue是多少,都要按照pKey = pValue格式写入linebuf中。
strcat(filebuf, linebuf);//将linebuf连接到filebuf中。
iTag = 1;//将iTag置,表示fp文件中存在pKey。
}
}

if(iTag == 0)//若不存在,则追加
{
fprintf(fp, "%s = %s\n", pKey, pValue);
}
else//若存在pKey
{
if(fp != NULL)
{
fclose(fp);
fp = NULL; //避免野指针
}
fp = fopen(pFileName, "w+t");

//(me)上面的这个先关闭然后又打开文件,其用意是为了让文件指针回到文件起始位置,这用下面这条语句也可以代替。
//fseek(fp, -length, SEEK_END);
if(fp == NULL)
{
rv = -4;
printf("func SetCfgItem() call fopen() err: \n",rv);
goto End;
}
fputs(filebuf, fp);//把filebuf中的字符串写入到由fp指向的文件中。
}

End:
if(fp != NULL)
{
fclose(fp);
}
return rv;
}
 

main.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#define DB_sid "oracle_sid"
#define DB_User "appuser"
#define DB_PassWord "appuser"
#define CFG_FILENAME "F:\\Cpp_ChuanZhi\\MyProject\\demo1\\demo1\\cfg.ini"
#include "config.h"

void main_menu()
{
printf("\n1 Test SetCfgItem(测试设置配置文件) ");
printf("\n2 Test GetCfgItem(测试读取配置文件) ");
printf("\n0 exit(0) (程序退出) ");
printf("\nplease enter your choice:[1/2/0]: ");
}

int Test_SetCfgItem()
{
int rv = 0;
int choice = 0;
char key[1024] ;
char value[1024];

memset(key, 0, sizeof(key));
memset(value, 0, sizeof(value));

printf("\nplease enter key:");
scanf("%s", key);

printf("\nplease enter value:");
scanf("%s", value);

//rv = SetCfgItem(CFG_FILENAME, key, value, strlen(value));
rv = SetCfgItem(CFG_FILENAME, key, value, NULL);
if (rv != 0)
{
printf("SetCfgItem() err: %d \n", rv);
goto End;
}
printf("读写配置项(绿灯) 测试通过\n");

End:
return rv;
}

int Test_GetCfgItem()
{
int rv = 0;
int choice = 0;
char key[1024] ;
char value[1024];
int valueLen = 1024;

memset(key, 0, sizeof(key));
memset(value, 0, sizeof(value));

printf("\nplease enter key:");
scanf("%s", key);

//printf("\nplease enter value:");
//scanf("%s", value);

rv = GetCfgItem(CFG_FILENAME, key, value, &valueLen);
if (rv != 0)
{
printf("SetCfgItem() err: %d \n", rv);
goto End;
}
printf("\n%s = %s", key, value);
printf("\n读写配置项(绿灯) 测试通过\n");

End:
return rv;
}

int main()
{
int rv = 0;
int choice = 0;

for(;;)
{
main_menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
Test_SetCfgItem(); break;
case 2:
Test_GetCfgItem(); break;
case 0:
exit(0);
default:
exit(0);
}
}

getchar();
return rv;
}
输出结果如下:
 






 

为了理清SetCfgItem()函数的逻辑顺序,这里画出了它的流程图如下。



 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C C++ 文件操作