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

 我们在程序编写中往往会遇到这样的情况,程序运行中我们会读取配置文件,从中读取我们程序需要的各种参数与配置信息,我一般采用读取配置文件和使用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"。因为实现比较容易,就不详细贴出实例了。

 

 

 

 

 

你可能感兴趣的:(c,File,null,search,语言,Parsing)