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

读取配置文件[方式一]之纯C语言实现

2010-01-11 23:51 645 查看
我们在程序编写中往往会遇到这样的情况,程序运行中我们会读取配置文件,从中读取我们程序需要的各种参数与配置信息,我一般采用读取配置文件和使用Berkeley db的方式,这两种方式的移植性都较好,当然前者更好,但是后者也有它的好处,它可以很好的处理配置信息的读取等等,但是它需要bdb库的支持,虽然库很小,但对我们来讲,还是麻烦,所以我们一般还是采用读取配置文件的方式,而读取配置文件的方式又有很多种,本篇就讲第一种方式,也是我最常用的一种方式,单纯的用C库函数来实现.
一.示例
这里我们还是以usb_modeswitch的代码为例子进行讲解,首先看一下它的配置文件:usb_modeswitch.conf,其中的格式如下部分所示:
########################################################
# Huawei E630
#
# There seem to be modem-only variants around - no storage,
# no switching
#
# Contributor: Joakim Wenrgren
;DefaultVendor=  0x1033
;DefaultProduct= 0x0035
;TargetVendor=   0x12d1
;TargetProduct=  0x1003
# choose one of these:
;HuaweiMode=1
;DetachStorageOnly=1
########################################################

二.读取此配置文件的程序实现

/////////////////////////////////////////////////////////
//
/**********************************************************
//真正的配置文件解析函数,它的实现很简单,就不详细分析了,主要就是利用几个标准库函数就可以处理了,
//主要包括:fgets,strchr ,strcspn ,strrchr ,strspn ,
//
//
**********************************************************/
/***************************************/
// the parameter parsing stuff
/***************************************/
char* ReadParseParam(const char* FileName, char *VariableName)
{
static char Str[LINE_DIM];
char *VarName, *Comment=NULL, *Equal=NULL;
char *FirstQuote, *LastQuote, *P1, *P2;
int Line=0, Len=0, Pos=0;
FILE *file=fopen(FileName, "r");

if (file==NULL) {
fprintf(stderr, "Error: Could not find file %s/n/n", FileName);
exit(1);
}

while (fgets(Str, LINE_DIM-1, file) != NULL) {
Line++;
Len=strlen(Str);
if (Len==0) goto Next;
if (Str[Len-1]=='/n' or Str[Len-1]=='/r') Str[--Len]='/0';
Equal = strchr (Str, '=');			// search for equal sign
Pos = strcspn (Str, ";#!");			// search for comment
Comment = (Pos==Len) ? NULL : Str+Pos;
if (Equal==NULL or ( Comment!=NULL and Comment<=Equal)) goto Next;	// Only comment
*Equal++ = '/0';
if (Comment!=NULL) *Comment='/0';

// String
FirstQuote=strchr (Equal, '"');		// search for double quote char
LastQuote=strrchr (Equal, '"');
if (FirstQuote!=NULL) {
if (LastQuote==NULL) {
fprintf(stderr, "Error reading parameter file %s line %d - Missing end quote./n", FileName, Line);
goto Next;
}
*FirstQuote=*LastQuote='/0';
Equal=FirstQuote+1;
}

// removes leading/trailing spaces
Pos=strspn (Str, " /t");
if (Pos==strlen(Str)) {
fprintf(stderr, "Error reading parameter file %s line %d - Missing variable name./n", FileName, Line);
goto Next;		// No function name
}
while ((P1=strrchr(Str, ' '))!=NULL or (P2=strrchr(Str, '/t'))!=NULL)
if (P1!=NULL) *P1='/0';
else if (P2!=NULL) *P2='/0';
VarName=Str+Pos;
//while (strspn(VarName, " /t")==strlen(VarName)) VarName++;

Pos=strspn (Equal, " /t");
if (Pos==strlen(Equal)) {
fprintf(stderr, "Error reading parameter file %s line %d - Missing value./n", FileName, Line);
goto Next;		// No function name
}
Equal+=Pos;

if (strcmp(VarName, VariableName)==0) {		// Found it
fclose(file);
return Equal;
}
Next:;
}

// not found
//	fprintf(stderr, "Error reading parameter file %s - Variable %s not found.",
//				FileName, VariableName);
fclose(file);
return NULL;
}

//
//
//
//*根据各种将要读取参数的类型,我们定义如下宏,用于各种不同的处理。*/
/*********************************************************************
注意:对于函数的带参宏定义,没有使用过的人可能会有疑问,在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。

***********************************************************************/
//////////
//
//
extern char* ReadParseParam(const char* FileName, char *VariableName);

extern char *TempPP;
//字符串参数
#define ParseParamString(ParamFileName, Str) /
if ((TempPP=ReadParseParam((ParamFileName), #Str))!=NULL) /
strcpy(Str, TempPP); else Str[0]='/0'
//整数参数
#define ParseParamInt(ParamFileName, Int) /
if ((TempPP=ReadParseParam((ParamFileName), #Int))!=NULL) /
Int=atoi(TempPP)
//16进制整数参数
#define ParseParamHex(ParamFileName, Int) /
if ((TempPP=ReadParseParam((ParamFileName), #Int))!=NULL) /
Int=strtol(TempPP, NULL, 16)
//浮点数参数
#define ParseParamFloat(ParamFileName, Flt) /
if ((TempPP=ReadParseParam((ParamFileName), #Flt))!=NULL) /
Flt=atof(TempPP)
//布尔型参数
#define ParseParamBool(ParamFileName, B) /
if ((TempPP=ReadParseParam((ParamFileName), #B))!=NULL) /
B=(toupper(TempPP[0])=='Y' || toupper(TempPP[0])=='T'|| TempPP[0]=='1'); else B=0

//
//
//*************************************************************************
////////////////////
根据不同的参数类型和参数内容的性质(string,int,hex,bool?),调用不同的函数,实际上是调用同一函数只是进行不同的处理而以,由于这些处理很容易,故直接在宏定义定义成不同的函数了,使程序的条理更加清晰。
***************************************************************************/
/////////////
void readConfigFile(const char *configFilename)
{
if (verbose) printf("Reading config file: %s/n", configFilename);
ParseParamHex(configFilename, TargetVendor);
ParseParamHex(configFilename, TargetProduct);
ParseParamString(configFilename, TargetProductList);
ParseParamHex(configFilename, TargetClass);
ParseParamHex(configFilename, DefaultVendor);
ParseParamHex(configFilename, DefaultProduct);
ParseParamBool(configFilename, DetachStorageOnly);
ParseParamBool(configFilename, HuaweiMode);
ParseParamBool(configFilename, SierraMode);
ParseParamBool(configFilename, SonyMode);
ParseParamBool(configFilename, GCTMode);
ParseParamHex(configFilename, MessageEndpoint);
ParseParamString(configFilename, MessageContent);
ParseParamHex(configFilename, NeedResponse);
ParseParamHex(configFilename, ResponseEndpoint);
ParseParamHex(configFilename, ResetUSB);
ParseParamHex(configFilename, InquireDevice);
ParseParamInt(configFilename, CheckSuccess);
ParseParamHex(configFilename, Interface);
ParseParamHex(configFilename, Configuration);
ParseParamHex(configFilename, AltSetting);
// TargetProductList has priority over TargetProduct
if (strlen(TargetProductList))
TargetProduct = 0;
config_read = 1;
}
//
//
////////
/*****************************************************************
main函数包括上一篇中讲到的读取命令行参数,如果命令行参数使用了-W等参数,则直接调用默认的配置文件,否则,后面的参数将由命令行传入。
******************************************************************/
//
//////////////////
int main(int argc, char **argv)
{
// Check command arguments, use params instead of config file when given
switch (readArguments(argc, argv)) {
case 0:                        // no argument or -W, -q or -s
readConfigFile("/etc/usb_modeswitch.conf");
break;
default:                    // one or more arguments except -W, -q or -s
if (!config_read)        // if arguments contain -c, the config file was already processed
if (verbose) printf("Taking all parameters from the command line/n/n");
}
}


三.以上程序思想

由于c标准库函数提供了一大把可用的函数调用供我们使用,所以我们可以使用各种可用的函数来实现以上功能,但其基本实现思想都是一样的:每次只读取一行,然后将这一行放入一个buffer中,,然后根据我们设置的配置文件格式进行字符串处理就OK了,其中主要就是依靠strtok函数返回这一buffer中的各段数据,比如 NAME = anson,则需要使用strtok函数返回"NAME","=","anson"三段,当然是依次来,前面如果与传入的参数项目"NAME"不匹配也就没有做下去的必要了,可以读取下一个参数项了。"NAME"是项目类型,“anson”则是项目值,如果传入的参数查询项与"NAME"匹配,则可以取后面的值"anson"。因为实现比较容易,就不详细贴出实例了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: