配置文件案例读写分析
/*
*运行平台: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;
}