1.config_rw.c
#include "config_rw.h"
#define filenm "./config"
/*
函数功能: 去除字符串首位的空格;
输入参数: char *strIn 要去除首尾空格的字符串;
返回参数: char *strOut 去除首尾空格后的字符串;
备注:
*/
static void trim(char *strIn, char *strOut)
{
char *start, *end, *temp;//定义去除空格后字符串的头尾指针和遍历指针
char *res;
int i = 0;
temp = strIn;
while (*temp == ' ' || *temp == '\t')
++temp;
start = temp; //求得头指针
temp = strIn + strlen(strIn) - 1; //得到原字符串最后一个字符的指针(不是'\0')
while (*temp == ' ' || *temp == '\t' || *temp == '\n')
--temp;
end = temp; //求得尾指针
for (i = 0, strIn = start; strIn <= end; i++)
{
*strOut++ = *strIn++;
}
*strOut = '\0';
strOut = strOut - i;
//printf("strout=%s ,length=%i,\n", strOut, strlen(strOut));
/*注意此处不能用指针直接将地址赋值,因为res是一个局部变量,与(windows不一样)
随着trim程序段运行结束被销毁,其地址内容也将销毁,指针中
的地址指向不确定的空间。而strcpy是将指针中的内容拷贝到另
一个地址中,因此不会被销毁。*/
//strOut=start;
}
/*
函数功能: 输入要查找的键,返回对应的值;
输入参数: char *line 读取到的一行;
char *key 要查询的键;
返回参数: char *value 查询到的键值;
备注: 正常找到键值返回0,配置文件格式不正确f返回-1,没有对应值返回-2。
*/
static int getValue(char *line, char *key, char *value)
{
char *temp = line;
if ((temp = strstr(line, key)) == NULL)
{
return -2;
}
if ((temp = strstr(temp, "=")) == NULL)
{
printf("配置文件格式有误\n缺少 “ = ”\n");
return -1;
}
temp += strlen("=");
trim(temp, value);
if (strlen(value) == 0)
{
printf("对应键值为空!\n");
return-1;
}
//printf("value=%s ,length=%i,\n", value, strlen(value));
return 0;
}
/*
函数功能: 读配置文件的每一行,并调用getvalue函数返回查找key的value
输入参数: const char *filename 配置文件路径;
char *key 查找的键;
输出参数: char *value 返回键值;
返回值: 0:在该行找到对应值
-1:配置文件中不存在该key或格式有误
-2:在该行未找到对应值
-3:文件路径有误
*/
int readCFG(const char *filename, char *key, char **value)
{
FILE *fp = NULL;
char line[1024] = { 0 }, vtemp[1024] = { 0 };
int get_flag = 0;
fp = fopen(filename, "r"); //以只读方式打开
if (!fp)
{
printf("Open File Error!\n");
return -3;
}
else
{
while (fgets(line, 1024, fp))
{
*value = NULL;
get_flag = getValue(line, key, vtemp);
//printf("getvalue=%i\n", get_flag);
if (get_flag == 0)
{
//printf("getvalue=0\n");
if (strlen(vtemp) != 0)
{
//memcpy(value, vtemp, strlen(vtemp));
*value = (char *)malloc(sizeof(char) * strlen(vtemp) + 1);
strcpy(*value, vtemp);
//printf("value3=%s ,length=%i,\n", *value, strlen(*value));
break;
}
else
*value = NULL;
}
else if (get_flag == -1)
{
printf("键 %s 对应的值为空或配置文件格式有误!\n", key);
return -1;
}
else if (get_flag == -2)
;
}
fclose(fp);
if (*value == NULL)
{
printf("没有找到 “ %s ”对应的键值!\n",key);
return -1;
}
}
//printf("value4=%s ,length=%i,\n",value,strlen(value));
return 0;
}
/*
函数功能: 向配置文件中写入参数
输入参数: const char *filename 待写入的目标文件
const char *key key值
const char *value value值
输出参数: -
返回值: 0:正确写入
-1:输入参数有误
-2:文件路径有误
-3:文件不能超过8K,不然内存不支持
*/
int writeCFG(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in*/)
{
int rv = 0, iTag = 0, length = 0;
FILE *fp = NULL;
char lineBuf[1024];
char *pTmp = NULL, *pBegin = NULL, *pEnd = NULL;
char filebuf[1024 * 8] = { 0 };
if (pFileName == NULL || pKey == NULL || pValue == NULL)
{
rv = -1;
printf("SetCfgItem() err. param err \n");
goto End;
}
fp = fopen(pFileName, "r+");
if (fp == NULL)
{
rv = -2;
printf("fopen() err. \n");
//goto End;
}
if (fp == NULL)//文件不存在
{
fp = fopen(pFileName, "w+t");
if (fp == NULL)
{
rv = -2;
printf("fopen() err. \n");
goto End;
}
}
fseek(fp, 0L, SEEK_END); //刚开始文件指针指向的是文件的开头,把文件指针从0位置开始,移动到文件末尾
//获取文件长度;
length = ftell(fp);//fp指向了文件的末尾
fseek(fp, 0L, SEEK_SET);//再把文件指针指向文件的头部
if (length > 1024 * 8) //文件不能超过8K,不然内存不支持
{
rv = -3;
printf("文件超过1024*8, nunsupport");
goto End;
}
while (!feof(fp))
{
//读每一行
memset(lineBuf, 0, sizeof(lineBuf));
pTmp = fgets(lineBuf, 1024, fp);
if (pTmp == NULL)
{
break;
}
//key关键字是否在本行
pTmp = strstr(lineBuf, pKey);
if (pTmp == NULL) //key关键字不在本行, copy到filebuf中
{
strcat(filebuf, lineBuf);//复制到filebuf中去
continue;
}
else //key关键在在本行中,替换旧的行,再copy到filebuf中
{
sprintf(lineBuf, "%s = %s\n", pKey, pValue);
strcat(filebuf, lineBuf);
//若存在key
iTag = 1;
}
}
//所有的行中若key关键字,不存在 追加
if (iTag == 0)
{
fprintf(fp, "%s = %s\n", pKey, pValue);//格式化输入
}
else //若key关键字,存在,则重新创建文件
{
if (fp != NULL)
{
fclose(fp); //关闭文件
fp = NULL; //避免野指针
}
fp = fopen(pFileName, "w+t"); //重新建立一个文件
if (fp == NULL)
{
rv = -2;
printf("fopen() err. \n");
goto End;
}
fputs(filebuf, fp);//把所有文件缓冲的内容输入到fp,覆盖原来的文件
//fwrite(filebuf, sizeof(char), strlen(filebuf), fp);
}
End:
if (fp != NULL)
{
fclose(fp);
}
return rv;
}
2.config_rw.h
#ifndef _CONFIG_RW_H
#define _CONFIG_RW_H
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#ifdef _cplusplus
extern "C" {
#endif
int readCFG(const char *filename, char *key, char **value);//
int writeCFG(char *pFileName /*in*/, char *pKey /*in*/, char * pValue/*in*/);
static void trim(char *strIn/*in*/, char *strOut/*oout*/);//去除字符串首尾的空格
static int getValue(char *keyAndValue/*in*/, char *key/*in*/, char *value/*out*/);//读键值对调用的函数
#ifdef _cplusplus
}
#endif
#endif
3.配置文件示例
ip=127.0.0.1
local_ipAddr= 127.0.0.1
dest_ipAddr=127.0.0.1
4.主程序
int main()
{
char *filename = "./config.txt";
char *valuee=0;
char* key = "ip";
if (readCFG(filename, key, &valuee) == 0)
printf("ip value=%s,length =%i\n", valuee, strlen(valuee));
if (readCFG(filename, "icdd", &valuee) == 0)
printf("icdd value=%s,length =%i\n", valuee, strlen(valuee));
if (writeCFG(filename, "jdjdjd", "220.336") == 0)
printf("写入成功\n");
if (writeCFG(filename, "ip", "220.336") == 0)
printf("写入成功\n");
getchar();
return 0;
}
5.关于传入参数**p的一点知识(转)
博客园
要修改变量的值,需要使用变量类型的指针作为参数或者变量的引用。如果变量是一般类型的变量,例如int,则需要使用int 类型的指针类型int 作为参数或者int的引用类型int&。但是如果变量类型是指针类型,例如char,那么需要使用该类型的指针,即指向指针的指针类型 char* ,或者该类型的引用类型char&。
首先要清楚 不管是指针还是值传入函数后都会创建一个副本,函数结束后值内容不能传出来是因为值的副本,而传入的值并没被修改,指针能传出来是因为我们修改的是指针指向的内容而不是指针指向的地址。
我们既然要举例子 就找一个比较经典的实用例子。
在我们进行内存管理的时候,如果想创建一个分配空间的函数,函数中调用了malloc方法申请一块内存区域。
先将一个错误的例子,如下:
void GetMemory1(char *p,int num)
{
p=malloc(sizeof(int)*num);
return;
}
void Test1()
{
char *p=NULL;
GetMemory(p);
}
上述例子 是很普通的 将一个指针作为参数来申请一个动态内存空间,可是这个程序是错误的。
错误的原因:
由于其中的*p实际上是Test1中p的一个副本,编译器总是要为函数的每个参数制作临时副本。在本例中,p申请了新的内存,只是把p所指向的内存地址改变了,但是Test1中p丝毫未变。因为函数GetMemory1没有返回值,因此Test1中p并不指向申请的那段内存。
因为malloc的工作机制是在堆中寻找一块可用内存区,返回指向被分配内存的指针。
所以这时p指向了这个申请的内存的地址。由于在指针作为传入参数的时候会在函数体中创建一个副本指针_p
_p指针和p指针的联系就是他们指向同一个内存区域,但是malloc的函数使得_p指向了另外一个内存区域,而这个内存区域并没有座位传出参数传给p,
所以p并没有发生任何改变,仍然为NULL。
如何才能解决上述问题呢?
使用下述办法可以解决问题:
void GetMemory2(char **p,int num)
{
- p=malloc(sizeof(int)*num);
return;
}
void Test2()
{
char *p=NULL;
GetMemory(&p);
}
下面开始分析GetMemory2()和 Test2()的原理:
char *p 可以进行拆分(从左向右拆分) char p ,所以可以认为p是一个char 的指针(注意这里不是p而是p)。那么p的内容就是一个指向char的指针的地址,换句话来说p是指向这个char指针的指针。
从test2可以看出 p是一个char的指针, &p则是这个char指针的地址,换句话来说 &p是指向这个char*p的指针的指针,与GetMemory2()定义相符。所以在调用时候要使用&p而不是p。
在GetMemory2()中 *p=malloc(sizeof(int)*num);
其中*p保存了 这个分配的内存的地址,那么p就是指向这个分配的内存地址的指针。
其实在为什么要用指针的指针的道理很简单:
因为VC内部机制是将函数的传入参数都做一个副本,如果我们传入的用来获取malloc分配内存地址的副本变化了,而我们的参数并不会同步,除非使用函数返回值的方式才能传出去。
所以我们就要找一个不变的可以并能用来获取malloc内存地址的参数,
如果能够创建另外一个指针A,这个指针指向GetMemory1(char * p,int …)中的传入参数指针p,那么 就算p的内容变了,而p的地址没变,我们仍然可以通过这个指针A来对应得到p的地址并获得p所指向的分配的内存地址,没错这个指针A就是本文想要讲的指向(char *)指针的指针(char **)。
于是我们创建了一个char 的指针p(注意这里不是char 的指针p),这个p作为传入参数,在进入函数后,系统会为该指针创建一个副本_p,我们让_p指向malloc分配的内存的地址(注意这里是*_p而不是_p),_p作为指向这个分配的内存地址指针的指针,这样在分配过程中_p并没有变化。
另外注意在void Test2()中的char *p 的p是指向的是malloc分配的内存的地址,通过GetMemory2()函数后&p作为传入参数没有变化被返回回来,依据&p将p指针指向了真正分配的内存空间。
最后还要注意一个要点,void Test2()中 GetMemory(&p);和void GetMemory2(char **p,int num)这个函数的定义,很容易有一个迷惑,在使用处的变量和定义处的变量是如何一一对应的。
下面来做解释:
不对应关系:其实这两个p不是一个p,&p中的p是一个char *的指针,而char**p中的p却是指向char *指针的指针。
对应关系:其实这个对应关系就是 在void Test2()调用的时候传入参数&p是指向char 指针的指针,GetMemory2()定义中的char **p也是指向char指针的指针,所以说 在调用时候传入的参数和在定义时候使用的传入参数必须是匹配的。
如果看了上面的文字还是比较混乱的话就用一句话来总结下重点:
1,VC的函数机制传入的参数都是会创建一个副本 不管是指针还是值;如果是值A则直接创建另外一个值B,值B拥有和A相同的值;如果是传入参指针C创建另外一个指针的副本D,那么D所指向的地址是和C相同的地址。
2,第1条总结可知指针传参比值传参的多出的功能是可以通过修改指针D所指向的地址的内容来讲函数的运算结果告知指针C,因为C和D指向相同的地址。
3,第2跳总结的指针C和D所共用的指向地址可以是值类型,也可以是另外一个指针的地址(也就是上面所讲的**)。