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