嵌入式項(xiàng)目開(kāi)發(fā)中,會(huì)有很多功能模塊需要頻繁修改參數(shù),Linux下我們可以通過(guò)ini格式的文件保存配置信息。
本文通過(guò)開(kāi)源庫(kù)iniparser,詳細(xì)講解如何用C語(yǔ)言實(shí)現(xiàn)ini文件的參數(shù)解析和配置保存。
本文代碼實(shí)例獲取方式見(jiàn)文末。
一、ini文件
1 什么是 ini文件
INI(Initialization File)文件是一種簡(jiǎn)單直觀的數(shù)據(jù)存儲(chǔ)格式,常用于配置應(yīng)用程序的初始化設(shè)置。這種文件通常包含若干個(gè)節(jié)(section)和鍵值對(duì)(key-value pairs)。 INI文件的每一部分都是自描述性的,易于閱讀和編輯,使得非程序員也能輕易理解并修改配置參數(shù)。
INI文件因其簡(jiǎn)單易用性而在許多編程語(yǔ)言中廣泛應(yīng)用,尤其是在Windows操作系統(tǒng)中,很多應(yīng)用程序都采用INI文件作為配置文件。 當(dāng)然,隨著XML、JSON等更豐富、更結(jié)構(gòu)化的數(shù)據(jù)交換格式的普及,INI文件在現(xiàn)代應(yīng)用程序中的使用相對(duì)減少,但在一些輕量級(jí)應(yīng)用或?qū)?dòng)速度有較高要求的情況下,仍然是一種常見(jiàn)且實(shí)用的配置文件格式。
2 ini文件結(jié)構(gòu)
節(jié)(Section):
INI文件中的各個(gè)部分通過(guò)方括號(hào) [] 包裹的名稱(chēng)來(lái)定義,例如 **[Section1]**。每個(gè)節(jié)可以包含多個(gè)鍵值對(duì)。
鍵值對(duì)(Key-Value Pairs):
鍵和值之間用等號(hào) = 分隔,如 key1=value1。鍵通常是描述性質(zhì)的字符串,而值則可以是字符串、數(shù)字或其他類(lèi)型的數(shù)據(jù)。
注釋:
注釋行以分號(hào) ; 開(kāi)始,直到行尾都被視為注釋內(nèi)容,不會(huì)被程序解析。
多行值:
某些INI解析器允許值跨越多行,通常通過(guò)在行尾添加反斜杠 \ 來(lái)延續(xù)到下一行
3 ini文件舉例
;author yikoupeng[BASIC_INFO]version = V1.1.1.1user = yikounumber = 999[FTP]ftppath = /home/ftpftpuser = ftpftppass = 123456port = 21......
其中:
- 注釋以分號(hào)(;)開(kāi)頭
- [BASIC_INFO]、[FTP]就是組名,
- 組成員有“version”........“ftppath”...
注意:
每個(gè)組下的key是唯一不能重復(fù)的,但不同組下可以存在相同的key.
二、 iniparser庫(kù)
1. iniparser介紹
iniparser是一個(gè)C語(yǔ)言庫(kù),用于解析和操作 INI 格式的配置文件,是針對(duì)INI文件的開(kāi)源解析器。
iniparser可以對(duì)配置文件進(jìn)行解析、添加、修改、刪除等操作。
git地址如下:
https://github.com/ndevilla/iniparser
2. iniparser的安裝
1、下載iniparser
wget https://codeload.github.com/ndevilla/iniparser/tar.gz/refs/tags/v4.1 -O iniparserv4.1.tar.gz
2、解壓
tar -zxvf iniparserv4.1.tar.gz
cd iniparser-4.1/
peng@ubuntu:~/work/iniparser-4.1$ lsAUTHORS doc example FAQ-en.md FAQ-zhcn.md html INSTALL libiniparser.a libiniparser.so.1 LICENSE Makefile README.md src test
3、進(jìn)入目錄
4、進(jìn)入src文件夾可以看到我們所需要的主要代碼
peng@ubuntu:~/work/iniparser-4.1$ cd src/peng@ubuntu:~/work/fdw/code/config/iniparser-4.1/src$ lsdictionary.c dictionary.h iniparser.c iniparser.h
如果想移植該程序到我們的項(xiàng)目中,只需要將這幾個(gè)文件添加到工程對(duì)應(yīng)目錄,編譯進(jìn)工程即可。
三、iniparser API(應(yīng)用編程程序接口)
dictionary.h里面聲明了一些直接解析ini file的API,iniparser.h頭文件里面聲明了一些提供用戶操作的API。
iniparser.h里面的API是對(duì)dictionary.h里面API的再次封裝,以提供用戶友好性。
iniparser.h頭文件里面的主要API
1 加載ini文件
/* * @brief 從ini格式的配置文件中加載數(shù)據(jù) * @param [IN] ininame 要打開(kāi)的ini格式文件 * @return != NULL 返回一個(gè)指向dictionary結(jié)構(gòu)的指針 * == NULL 加載ini文件失敗 */ dictionary * iniparser_load(const char *ininame);
2 獲取鍵值
/* * @brief 獲取指定鍵(key)對(duì)應(yīng)的字符串類(lèi)型的值 * @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] key 要查找的鍵,通常格式為 "section:key",表示要獲取哪個(gè)節(jié)(section)下的哪一項(xiàng)(key)的值。 * @param [IN] def 當(dāng)鍵不存在或者其值不是字符串時(shí)的默認(rèn)返回值。如果沒(méi)有找到對(duì)應(yīng)鍵,函數(shù)將返回此默認(rèn)值。 * @return 如果找到了相應(yīng)的鍵,返回鍵值對(duì)應(yīng)字符串 * 如果沒(méi)有找到匹配的鍵,返回def指定的字符串值 */ const char * iniparser_getstring(const dictionary *d, const char *key, const char *def); /* * @brief 獲取指定鍵(key)對(duì)應(yīng)的整數(shù)值 * @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] key 要查找的鍵,通常格式為 "section:key",表示要獲取哪個(gè)節(jié)(section)下的哪一項(xiàng)(key)的值。 * @param [IN] notfound 當(dāng)鍵不存在或者其值不能被轉(zhuǎn)換為整數(shù)時(shí),函數(shù)將返回這個(gè)默認(rèn)值。 * @return 如果找到了相應(yīng)的鍵,并且其值可以被成功轉(zhuǎn)換為整數(shù),則返回該整數(shù)值。 * 如果沒(méi)有找到匹配的鍵,或者該鍵對(duì)應(yīng)的值無(wú)法轉(zhuǎn)換為整數(shù),則返回 notfound 參數(shù)提供的默認(rèn)值。 */ int iniparser_getint(const dictionary * d, const char * key, int notfound); /* * @brief 獲取指定鍵(key)對(duì)應(yīng)的浮點(diǎn)型值 * @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] key 要查找的鍵,通常格式為 "section:key",表示要獲取哪個(gè)節(jié)(section)下的哪一項(xiàng)(key)的值。 * @param [IN] notfound 當(dāng)鍵不存在或者其值無(wú)法轉(zhuǎn)換為雙精度浮點(diǎn)數(shù)時(shí),函數(shù)返回的默認(rèn)值。 * @return 如果找到了相應(yīng)的鍵,并且其值能成功轉(zhuǎn)換為一個(gè)雙精度浮點(diǎn)數(shù),則返回該浮點(diǎn)數(shù)。 * 如果沒(méi)有找到匹配的鍵,或者鍵的值不能被解釋為一個(gè)有效的雙精度浮點(diǎn)數(shù),則返回 notfound 參數(shù)所提供的默認(rèn)值。 */ double iniparser_getdouble(const dictionary *d, const char *key, double notfound);
3 設(shè)置鍵值
/* * @brief 設(shè)置或修改 ini 配置文件中某個(gè)鍵值對(duì) * @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] entry 字符串形式的鍵值對(duì)標(biāo)識(shí)符,格式通常是 "section:key",表明您要在哪個(gè)節(jié)(section)下的哪個(gè)鍵(key)上設(shè)置或修改值(val)。 * key值存在則修改對(duì)應(yīng)val,key值不存在則會(huì)新增 * @param [IN] val: 要設(shè)置的新值,作為字符串傳遞。 * @return 返回0表示設(shè)置成功 */ int iniparser_set(dictionary *ini, const char *entry, const char *val);
4 移除鍵值
/** @brief 移除 ini 配置文件中某個(gè)鍵值對(duì)* @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] entry 字符串形式的鍵名,包括可選的部分名稱(chēng)(section)和鍵(key)* 如果不指定key,則會(huì)移除整個(gè)section*/void iniparser_unset(ini, const char *entry);
5 判斷鍵是否存在
/** @brief 判斷 ini 配置文件是否存在某個(gè)鍵值* @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] entry 字符串形式的鍵值對(duì)標(biāo)識(shí)符,格式通常是 "section:key"* @return 返回1表示存在,返回0表示不存在*/int iniparser_find_entry(const dictionary *ini, const char *entry);
6 獲取section個(gè)數(shù)
/** @brief 獲取ini配置文件中section的數(shù)量* @param [IN] d dictionary結(jié)構(gòu)的指針 * @return 成功返回section個(gè)數(shù),失敗返回 -1*/int iniparser_getnsec(const dictionary * d); /** @brief 獲取某個(gè)section值* @param [IN] d dictionary結(jié)構(gòu)的指針* @param [IN] n 指定獲取第幾個(gè)section值 * @return 成功返回獲取到的section值,失敗返回NULL*/const char *iniparser_getsecname(const dictionary * d, int n);
7 獲取section下key個(gè)數(shù)
/** @brief 獲取ini配置文件中某個(gè)section的key個(gè)數(shù)* @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] s section * @return 返回指定section下的key個(gè)數(shù)*/int iniparser_getsecnkeys(dictionary * d, char * s); /** @brief 獲取ini配置文件中某個(gè)section的所有key* @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] s section * @param [OUT] keys 通過(guò)這個(gè)參數(shù)輸出key,也可以通過(guò)返回值獲取 * @return 成功返回指定section下的key,失敗返回NULL*/const char **iniparser_getseckeys(const dictionary *d, const char *s, const char **keys)
8 保存dictionary對(duì)象到文件中
/** @brief 保存dictionary對(duì)象到文件中* @param [IN] d dictionary結(jié)構(gòu)的指針 * @param [IN] f 已打開(kāi)的文件描述符*/void iniparser_dump_ini(const dictionary *d, FILE *f);
9 釋放dictionary對(duì)象
/** @brief 釋放dictionary對(duì)象* @param [IN] d dictionary結(jié)構(gòu)的指針 */void iniparser_freedict(dictionary * d);
10 api匯總
- iniparser.h頭文件里面的API
//獲取dictionary對(duì)象的section個(gè)數(shù) int iniparser_getnsec(dictionary * d); //獲取dictionary對(duì)象的第n個(gè)section的名字 char * iniparser_getsecname(dictionary * d, int n); //保存dictionary對(duì)象到file void iniparser_dump_ini(dictionary * d, FILE * f); //保存dictionary對(duì)象一個(gè)section到filevoid iniparser_dumpsection_ini(dictionary * d, char * s, FILE * f); //保存dictionary對(duì)象到file void iniparser_dump(dictionary * d, FILE * f); //獲取dictionary對(duì)象某個(gè)section下的key個(gè)數(shù) int iniparser_getsecnkeys(dictionary * d, char * s); //獲取dictionary對(duì)象某個(gè)section下所有的keychar ** iniparser_getseckeys(dictionary * d, char * s); //返回dictionary對(duì)象的section:key對(duì)應(yīng)的字串值 char * iniparser_getstring(dictionary * d, const char * key, char * def); //返回idictionary對(duì)象的section:key對(duì)應(yīng)的整形值 int iniparser_getint(dictionary * d, const char * key, int notfound); //返回dictionary對(duì)象的section:key對(duì)應(yīng)的雙浮點(diǎn)值 double iniparser_getdouble(dictionary * d, const char * key, double notfound); //返回dictionary對(duì)象的section:key對(duì)應(yīng)的布爾值 int iniparser_getboolean(dictionary * d, const char * key, int notfound); //設(shè)置dictionary對(duì)象的某個(gè)section:key的值 int iniparser_set(dictionary * ini, const char * entry, const char * val); //刪除dictionary對(duì)象中某個(gè)section:keyvoid iniparser_unset(dictionary * ini, const char * entry); //判斷dictionary對(duì)象中是否存在某個(gè)section:keyint iniparser_find_entry(dictionary * ini, const char * entry) ; //解析dictionary對(duì)象并返回(分配內(nèi)存)dictionary對(duì)象dictionary * iniparser_load(const char * ininame);//釋放dictionary對(duì)象(內(nèi)存) void iniparser_freedict(dictionary * d);
- dictionary.h頭文件里面的API
//計(jì)算關(guān)鍵詞的hash值unsigned dictionary_hash(const char * key); //創(chuàng)建dictionary對(duì)象dictionary * dictionary_new(int size); //刪除dictionary對(duì)象void dictionary_del(dictionary * vd); //獲取dictionary對(duì)象的key值char * dictionary_get(dictionary * d, const char * key, char * def); //設(shè)置dictionary對(duì)象的key值int dictionary_set(dictionary * vd, const char * key, const char * val); //刪除dictionary對(duì)象的key值void dictionary_unset(dictionary * d, const char * key); //保存dictionary對(duì)象void dictionary_dump(dictionary * d, FILE * out);
四、 iniparser庫(kù)C語(yǔ)言操作實(shí)例
1、config.ini
編寫(xiě)配置文件:
vim config.ini
[BASIC_INFO]version = V1.1.1.1user = yikounumber = 999[FTP]ftppath = /home/ftpftpuser = ftpftppass = 123456port = 21[NETWORK]interface = eth1dns1 = 8.8.8.8dns2 = 8.8.8.8subnet = 255.255.255.0router = 192.168.3.1
2、讀取配置參數(shù)
嘗試編寫(xiě)iniparser程序?qū)ni文件進(jìn)行修改:
#include #include "iniparser.h"#include "dictionary.h"#define PATH "config.ini"typedef unsigned char BYTE;typedef unsigned char UINT8;typedef unsigned char UCHAR;typedef unsigned short int UINT16;typedef unsigned long int UINT32;struct device_cfg_s{/*basicinfo*/ char version[32]; char user[32]; int number;/*ftp*/ char ftppath[128]; char ftpuser[32]; char ftppass[32]; UINT16 port;/*network*/ char interface[16]; char dns1[32]; char dns2[32]; char subnet[32]; char router[32]; };struct device_cfg_s devcfg;int cfg_load(char *name){ dictionary *ini= NULL; /* 解析dictionary對(duì)象并返回(分配內(nèi)存)dictionary對(duì)象*/ ini = iniparser_load(name); if( ini ==NULL) { printf("iniparser failure\n"); return -1; } /*basicinfo*/ strcpy(devcfg.version,iniparser_getstring(ini, "BASIC_INFO:version", "v0.0.0.0")); strcpy(devcfg.user ,iniparser_getstring(ini, "BASIC_INFO:user", "yikou")); devcfg.number = iniparser_getint(ini, "BASIC_INFO:number", 666); /*ftp*/ strcpy(devcfg.ftppath ,iniparser_getstring(ini, "FTP:ftppath", "/")); strcpy(devcfg.ftpuser ,iniparser_getstring(ini, "FTP:ftpuser", "ftp")); strcpy(devcfg.ftppass ,iniparser_getstring(ini, "FTP:ftppass", "123456")); devcfg.port = iniparser_getint(ini, "FTP:port", 21); /*network*/ strcpy(devcfg.interface,iniparser_getstring(ini, "NETWORK:interface", "eth0")); strcpy(devcfg.dns1,iniparser_getstring(ini, "NETWORK:dns1", NULL)); strcpy(devcfg.dns2 ,iniparser_getstring(ini, "NETWORK:dns2", NULL)); strcpy(devcfg.subnet ,iniparser_getstring(ini, "NETWORK:subnet", "255.255.255.0")); strcpy(devcfg.router ,iniparser_getstring(ini, "NETWORK:router", "192.168.3.1")); /* 返回dictionary對(duì)象的section,key對(duì)應(yīng)的字串值 */ printf("version:%s\n",devcfg.version); printf("user:%s\n", devcfg.user); printf("number:%d\n",devcfg.number); printf("ftppath:%s\n", devcfg.ftppath); printf("ftpuser:%s\n",devcfg.ftpuser); printf("ftppass:%s\n", devcfg.ftppass); printf("port:%d\n",devcfg.port); printf("interface:%s\n", devcfg.interface); printf("dns1:%s\n",devcfg.dns1); printf("dns2:%s\n", devcfg.dns2); printf("subnet:%s\n",devcfg.subnet); printf("router:%s\n", devcfg.router); iniparser_freedict(ini);}int main (int argc, char **argv){ cfg_load(PATH); //cfg_save_key(PATH,"BASIC_INFO","chnAddr","3501"); //cfg_save_key(PATH,"BASIC_INFO","enddeviceNo","87564289"); return 0;}
int cfg_save_key(char *filename,char *section,char *key,char *value)參數(shù): filename 配置文件名 section 節(jié)名字 key 鍵 value 值
int cfg_save_key(char *filename,char *section,char *key,char *value){ FILE *fp = NULL ; dictionary *ini= NULL; char item[128]={0}; /* 解析dictionary對(duì)象并返回(分配內(nèi)存)dictionary對(duì)象*/ ini = iniparser_load(filename); if( ini ==NULL) { printf("iniparser failure\n"); return -1; } sprintf(item,"%s:%s",section,key); /* 設(shè)置dictionary對(duì)象的某個(gè)section:key的值 */ iniparser_set(ini, item, value); fp = fopen(filename, "w"); if( fp == NULL ) { printf("stone:fopen error!\n"); exit(-1); } /* 保存dictionary對(duì)象 */ // iniparser_dumpsection_ini(ini, "BASIC_INFO", fp); iniparser_dump_ini(ini, fp); fclose(fp); /* 釋放dictionary對(duì)象(內(nèi)存)*/ iniparser_freedict(ini);}
可以看到最終值以配置文件中的為準(zhǔn)。
如果配置文件沒(méi)有配置參數(shù)則以iniparser_getxxxxx()中默認(rèn)值為準(zhǔn)。
3、保存配置信息到文件
為方便保存鍵值,彭老師封裝了函數(shù)
例如我們修改BASIC_INFO節(jié)的user的值為yikoupeng,number值為12345
cfg_save_key(PATH,"BASIC_INFO","user","yikoupeng"); cfg_save_key(PATH,"BASIC_INFO","number","12345");
執(zhí)行結(jié)果:
可以看到配置文件basic_info節(jié)的user 和number 鍵值對(duì)被修改。