我们在程序编写中往往会遇到这样的情况,程序运行中我们会读取配置文件,从中读取我们程序需要的各种参数与配置信息,我一般采用读取配置文件和使用Berkeley db的方式,这两种方式的移植性都较好,当然前者更好,但是后者也有它的好处,它可以很好的处理配置信息的读取等等,但是它需要bdb库的支持,虽然库很小,但对我们来讲,还是麻烦,所以我们一般还是采用读取配置文件的方式,而读取配置文件的方式又有很多种,本篇就讲第一种方式,也是我最常用的一种方式,单纯的用C库函数来实现.
一.示例
这里我们还是以usb_modeswitch的代码为例子进行讲解,首先看一下它的配置文件:usb_modeswitch.conf,其中的格式如下部分所示:
[c-sharp] view plain copy
- ########################################################
- # 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"。因为实现比较容易,就不详细贴出实例了。
我们在程序编写中往往会遇到这样的情况,程序运行中我们会读取配置文件,从中读取我们程序需要的各种参数与配置信息,我一般采用读取配置文件和使用Berkeley db的方式,这两种方式的移植性都较好,当然前者更好,但是后者也有它的好处,它可以很好的处理配置信息的读取等等,但是它需要bdb库的支持,虽然库很小,但对我们来讲,还是麻烦,所以我们一般还是采用读取配置文件的方式,而读取配置文件的方式又有很多种,本篇就讲第一种方式,也是我最常用的一种方式,单纯的用C库函数来实现.
一.示例
这里我们还是以usb_modeswitch的代码为例子进行讲解,首先看一下它的配置文件:usb_modeswitch.conf,其中的格式如下部分所示:
[c-sharp] view plain copy
- ########################################################
- # 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"。因为实现比较容易,就不详细贴出实例了。