1 ini_parse移植 1.1 下载ini解析源码 源码的github地址https://github.com/benhoyt/ini 。
1 git clone https://github.com/benhoyt/inih.git
1.2 使用ini_parse功能
可以看到核心就是一个ini_parse函数。用户自定义一个callback函数去解析自己的配置ini。测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <stdio.h> #include "ini.h" #include <string.h> typedef struct _SAMPLE_INI_CFG_S { char name[100 ]; int bus_id; int age; char name2[100 ]; int bus_id2; int age2; } SAMPLE_INI_CFG_S; static int parse_handler (void *user, const char *section, const char *name, const char *value) { SAMPLE_INI_CFG_S *cfg = (SAMPLE_INI_CFG_S *)user; if (strcmp (section, "person1" ) == 0 ) { if (strcmp (name, "name" ) == 0 ) { strcpy (cfg->name, value); } else if (strcmp (name, "bus_id" ) == 0 ) { cfg->bus_id = atoi(value); } else if (strcmp (name, "age" ) == 0 ) { cfg->age = atoi(value); } else { } } else if (strcmp (section, "person2" ) == 0 ) { if (strcmp (name, "name" ) == 0 ) { strcpy (cfg->name2, value); } else if (strcmp (name, "bus_id" ) == 0 ) { cfg->bus_id2 = atoi(value); } else if (strcmp (name, "age" ) == 0 ) { cfg->age2 = atoi(value); } else { } } else { } return 1 ; } int main (int argc, char **argv) { SAMPLE_INI_CFG_S ini_cfg; int ret = ini_parse("./sensor_cfg.ini" , parse_handler, &ini_cfg); if (ret > 0 ) { printf ("Parse err in %d line.\n" , ret); return ret; } if (ret == 0 ) { printf ("Parse incomplete, use default cfg ./sensor_cfg.ini\n" ); printf ("%s, %d, %d\n" , ini_cfg.name, ini_cfg.bus_id, ini_cfg.age); printf ("%s, %d, %d\n" , ini_cfg.name2, ini_cfg.bus_id2, ini_cfg.age2); } return 0 ; }
1.2.1 自定义ini文件 ini配置文件sensor_cfg.ini如下:
gcc test.c ini.c
。我的callback定义是parse_handler
,从ini中解析section,每个section会调用一次callback,解析出所有的section。
运行代码:
1.2.2 支持语法检测 ini_parse还支持语法检测。但ini写的不和语法规范会报错。手工制造ini语法错误,测试结果如下:
2 minIni移植使用 MiniINI 是一个用来解析 INI/CFG 配置文件的 C++ 库,主要特点是可移植性、性能和小体积。支持上千种 INI 格式配置,易用简单。
2.1 下载minIni GitHub - compuphase/minIni: A small and portable INI file library with read/write support
2.2 特征
minIni 支持读取senction外部的key,因此它支持不使用section的配置文件(但在其他方面与 INI 文件兼容)。
可以使用冒号分隔键和值;冒号等价于等号。也就是说,字符串“Name: Value”和“Name=Value”具有相同的含义。
minIni 不需要标准 C/C++ 库中的 文件 I/O 函数,且允许通过宏配置要选择文件 I/O 接口。
哈希字符 (“#”) 是分号开始注释的替代方法。允许尾随注释(即在一行上的键/值对后面)。
key名称和val周围的前导和尾随空格将被忽略。
当写入包含注释字符(“;”或“#”)的值时,该值将自动放在双引号之间;读取值时,将删除这些引号。当设置中出现双引号本身时,这些字符将被转义。
支持section和key枚举。
您可以选择设置 minIni 将使用的行终止符(对于文本文件)。(这是编译时设置,而不是运行时设置)。
由于写入速度远低于闪存(SD/MMC 卡、U 盘)中的读取速度,因此 minIni 以双倍“文件读取”为代价将“文件写入”降至最低。
内存占用是确定性的。没有动态内存分配。
2.3 INI 文件语法 1 2 3 4 [Network] hostname=My Computer address=dhcp dns = 192.168 .1 .1
2.4 minIni支持文件系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> #define INI_FILETYPE FILE* #define ini_openread(filename,file) ((*(file) = fopen((filename),"r" )) != NULL) #define ini_openwrite(filename,file) ((*(file) = fopen((filename),"w" )) != NULL) #define ini_close(file) (fclose(*(file)) == 0) #define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) #define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) #define ini_rename(source,dest) (rename((source), (dest)) == 0) #define ini_remove(filename) (remove(filename) == 0) #define INI_FILEPOS fpos_t #define ini_tell(file,pos) (fgetpos(*(file), (pos)) == 0) #define ini_seek(file,pos) (fsetpos(*(file), (pos)) == 0)
2.5 minIni的API介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <assert.h> #include <stdio.h> #include <string.h> #include "minIni.h" #define sizearray(a) (sizeof(a) / sizeof((a)[0])) const char inifile[] = "example.ini" ;int main (void ) { char str[100 ]; char section[50 ]; long n; n = ini_gets("Network" , "address" , "dummy" , str, sizearray(str), inifile); if (n >= 0 ) printf ("Network/address=%s" , str); n = ini_getl("Network" , "timeout" , -1 , inifile); printf ("Network/timeout=%ld\n" , n); }
example.ini如下:
1 2 3 4 5 [Network] hostname=My Computer address=dhcp dns=192.168 .1 .1 timeout=10
运行结果:
1 2 Network/address=dhcp Network/timeout=10
2.5.1 ini_gets() 获取字符串类型的值.
1 2 3 4 5 6 7 8 9 10 int ini_gets (const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename) ;
先打开文件,然后用 getkeystring()
找到目标键值,最后拷贝给调用者。
2.5.1.1 getkeystring
用 fgets 进行逐行读取,用 strrchr 找到包含 ‘[‘ 和 ‘]’ 的行,然后再用 strncasecmp 找到目标 Section 所在的行。
继续用 fgets 进行逐行读取,用 strrchr 找到包含 ‘=’ 的行,然后再用 strncasecmp 找到目标 Key 所在的行。
用 strncpy 将目标 Key 的值拷贝给调用者。
大致就是这3个关键步骤,当然还有很多其他异常处理,语法检测和边界判断的逻辑,这里不做展示。
2.5.2 ini_getl() ini_getl()
用于获取整型类型的值,也是间接调用int_gets
, 最后将字符串转换成数字。
2.5.3 ini_puts() 写出参数到ini,保存到ini。
1 2 3 4 5 6 7 8 9 10 11 12 int ini_puts (const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) ini_putl ("second" , "age" , 20 , inifile) ;n = ini_puts("first" , "alt" , NULL , inifile);
2.5.4 ini_putl() ini_putl()
用于写出整型类型的值,也是间接调用int_puts
, 将数字转换成字符串,然后保存字符串到ini。
2.5.5 section/key enumeration 1 2 3 4 5 6 7 8 printf ("4. Section/key enumeration, file structure follows\n" );for (s = 0 ; ini_getsection(s, section, sizearray(section), inifile) > 0 ; s++) { printf (" [%s]\n" , section); for (k = 0 ; ini_getkey(section, k, str, sizearray(str), inifile) > 0 ; k++) { printf ("\t%s\n" , str); } }
2.5.6 section/key存在性检查 1 2 3 4 5 6 assert(ini_hassection("first" , inifile)); assert(!ini_hassection("fourth" , inifile)); assert(ini_haskey("first" , "val" , inifile)); assert(!ini_haskey("first" , "test" , inifile)); printf ("5. checking presence of sections and keys passed\n" );
2.5.7 配置文件阅读打印 ini_browse
用来打印出每个段的每个key的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 int Callback (const char *section, const char *key, const char *value, void *userdata) { (void )userdata; printf (" [%s]\t%s=%s\n" , section, key, value); return 1 ; } printf ("6. browse through all settings, file field list follows\n" );ini_browse(Callback, NULL , inifile); if (access(filename, F_OK) != 0 ) { perror("open %s fail" , filename); }