一文告訴你Linux下如何用C語言實現ini配置文件的解析和保存
嵌入式項目開發中,會有很多功能模塊需要頻繁修改參數,Linux下我們可以通過ini格式的文件保存配置信息。
本文通過開源庫iniparser,詳細講解如何用C語言實現ini文件的參數解析和配置保存。
本文代碼實例獲取方式見文末。
一、ini文件
1 什么是 ini文件
- INI(Initialization File)文件是一種簡單直觀的數據存儲格式,常用于配置應用程序的初始化設置。這種文件通常包含若干個節(section)和鍵值對(key-value pairs)。INI文件的每一部分都是自描述性的,易于閱讀和編輯,使得非程序員也能輕易理解并修改配置參數。
- INI文件因其簡單易用性而在許多編程語言中廣泛應用,尤其是在Windows操作系統中,很多應用程序都采用INI文件作為配置文件。當然,隨著XML、JSON等更豐富、更結構化的數據交換格式的普及,INI文件在現代應用程序中的使用相對減少,但在一些輕量級應用或對啟動速度有較高要求的情況下,仍然是一種常見且實用的配置文件格式。
2 ini文件結構
-
節(Section):
INI文件中的各個部分通過方括號 [] 包裹的名稱來定義,例如 [Section1]。每個節可以包含多個鍵值對。
-
鍵值對(Key-Value Pairs):
鍵和值之間用等號 = 分隔,如 key1=value1。鍵通常是描述性質的字符串,而值則可以是字符串、數字或其他類型的數據。
-
注釋:
注釋行以分號 ; 開始,直到行尾都被視為注釋內容,不會被程序解析。
-
多行值:
某些INI解析器允許值跨越多行,通常通過在行尾添加反斜杠 \ 來延續到下一行
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是唯一不能重復的,但不同組下可以存在相同的key.
二、 iniparser庫
1. iniparser介紹
iniparser是一個C語言庫,用于解析和操作 INI 格式的配置文件,是針對INI文件的開源解析器。
iniparser可以對配置文件進行解析、添加、修改、刪除等操作。
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、進入目錄
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、進入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
如果想移植該程序到我們的項目中,只需要將這幾個文件添加到工程對應目錄,編譯進工程即可。
三、iniparser API(應用編程程序接口)
dictionary.h里面聲明了一些直接解析ini file的API,iniparser.h頭文件里面聲明了一些提供用戶操作的API。
iniparser.h里面的API是對dictionary.h里面API的再次封裝,以提供用戶友好性。
iniparser.h頭文件里面的主要API
1 加載ini文件
/*
* @brief 從ini格式的配置文件中加載數據
* @param [IN] ininame 要打開的ini格式文件
* @return != NULL 返回一個指向dictionary結構的指針
* == NULL 加載ini文件失敗
*/
dictionary * iniparser_load(const char *ininame);
2 獲取鍵值
-
/* * @brief 獲取指定鍵(key)對應的字符串類型的值 * @param [IN] d dictionary結構的指針 * @param [IN] key 要查找的鍵,通常格式為 "section:key",表示要獲取哪個節(section)下的哪一項(key)的值。 * @param [IN] def 當鍵不存在或者其值不是字符串時的默認返回值。如果沒有找到對應鍵,函數將返回此默認值。 * @return 如果找到了相應的鍵,返回鍵值對應字符串 * 如果沒有找到匹配的鍵,返回def指定的字符串值 */ const char * iniparser_getstring(const dictionary *d, const char *key, const char *def); /* * @brief 獲取指定鍵(key)對應的整數值 * @param [IN] d dictionary結構的指針 * @param [IN] key 要查找的鍵,通常格式為 "section:key",表示要獲取哪個節(section)下的哪一項(key)的值。 * @param [IN] notfound 當鍵不存在或者其值不能被轉換為整數時,函數將返回這個默認值。 * @return 如果找到了相應的鍵,并且其值可以被成功轉換為整數,則返回該整數值。 * 如果沒有找到匹配的鍵,或者該鍵對應的值無法轉換為整數,則返回 notfound 參數提供的默認值。 */ int iniparser_getint(const dictionary * d, const char * key, int notfound); /* * @brief 獲取指定鍵(key)對應的浮點型值 * @param [IN] d dictionary結構的指針 * @param [IN] key 要查找的鍵,通常格式為 "section:key",表示要獲取哪個節(section)下的哪一項(key)的值。 * @param [IN] notfound 當鍵不存在或者其值無法轉換為雙精度浮點數時,函數返回的默認值。 * @return 如果找到了相應的鍵,并且其值能成功轉換為一個雙精度浮點數,則返回該浮點數。 * 如果沒有找到匹配的鍵,或者鍵的值不能被解釋為一個有效的雙精度浮點數,則返回 notfound 參數所提供的默認值。 */ double iniparser_getdouble(const dictionary *d, const char *key, double notfound);
3 設置鍵值
/*
* @brief 設置或修改 ini 配置文件中某個鍵值對
* @param [IN] d dictionary結構的指針
* @param [IN] entry 字符串形式的鍵值對標識符,格式通常是 "section:key",表明您要在哪個節(section)下的哪個鍵(key)上設置或修改值(val)。
* key值存在則修改對應val,key值不存在則會新增
* @param [IN] val: 要設置的新值,作為字符串傳遞。
* @return 返回0表示設置成功
*/
int iniparser_set(dictionary *ini, const char *entry, const char *val);
4 移除鍵值
-
/* * @brief 移除 ini 配置文件中某個鍵值對 * @param [IN] d dictionary結構的指針 * @param [IN] entry 字符串形式的鍵名,包括可選的部分名稱(section)和鍵(key) * 如果不指定key,則會移除整個section */ void iniparser_unset(ini, const char *entry);
5 判斷鍵是否存在
-
/* * @brief 判斷 ini 配置文件是否存在某個鍵值 * @param [IN] d dictionary結構的指針 * @param [IN] entry 字符串形式的鍵值對標識符,格式通常是 "section:key" * @return 返回1表示存在,返回0表示不存在 */ int iniparser_find_entry(const dictionary *ini, const char *entry);
6 獲取section個數
-
/* * @brief 獲取ini配置文件中section的數量 * @param [IN] d dictionary結構的指針 * @return 成功返回section個數,失敗返回 -1 */ int iniparser_getnsec(const dictionary * d); /* * @brief 獲取某個section值 * @param [IN] d dictionary結構的指針 * @param [IN] n 指定獲取第幾個section值 * @return 成功返回獲取到的section值,失敗返回NULL */ const char *iniparser_getsecname(const dictionary * d, int n);
7 獲取section下key個數
-
/* * @brief 獲取ini配置文件中某個section的key個數 * @param [IN] d dictionary結構的指針 * @param [IN] s section * @return 返回指定section下的key個數 */ int iniparser_getsecnkeys(dictionary * d, char * s); /* * @brief 獲取ini配置文件中某個section的所有key * @param [IN] d dictionary結構的指針 * @param [IN] s section * @param [OUT] keys 通過這個參數輸出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結構的指針 * @param [IN] f 已打開的文件描述符 */ void iniparser_dump_ini(const dictionary *d, FILE *f);
9 釋放dictionary對象
-
/* * @brief 釋放dictionary對象 * @param [IN] d dictionary結構的指針 */ void iniparser_freedict(dictionary * d);
10 api匯總
- iniparser.h頭文件里面的API
//獲取dictionary對象的section個數
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個數
int iniparser_getsecnkeys(dictionary * d, char * s);
//獲取dictionary對象某個section下所有的key
char ** iniparser_getseckeys(dictionary * d, char * s);
//返回dictionary對象的section:key對應的字串值
char * iniparser_getstring(dictionary * d, const char * key, char * def);
//返回idictionary對象的section:key對應的整形值
int iniparser_getint(dictionary * d, const char * key, int notfound);
//返回dictionary對象的section:key對應的雙浮點值
double iniparser_getdouble(dictionary * d, const char * key, double notfound);
//返回dictionary對象的section:key對應的布爾值
int iniparser_getboolean(dictionary * d, const char * key, int notfound);
//設置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對象并返回(分配內存)dictionary對象
dictionary * iniparser_load(const char * ininame);
//釋放dictionary對象(內存)
void iniparser_freedict(dictionary * d);
- dictionary.h頭文件里面的API
//計算關鍵詞的hash值
unsigned dictionary_hash(const char * key);
//創建dictionary對象
dictionary * dictionary_new(int size);
//刪除dictionary對象
void dictionary_del(dictionary * vd);
//獲取dictionary對象的key值
char * dictionary_get(dictionary * d, const char * key, char * def);
//設置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、讀取配置參數
嘗試編寫iniparser程序對ini文件進行修改:
#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對象并返回(分配內存)dictionary對象*/
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對象的section,key對應的字串值 */
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;
}

可以看到最終值以配置文件中的為準。
如果配置文件沒有配置參數則以iniparser_getxxxxx()中默認值為準。
3、保存配置信息到文件
為方便保存鍵值,彭老師封裝了函數
int cfg_save_key(char *filename,char *section,char *key,char *value)
參數:
filename 配置文件名
section 節名字
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對象并返回(分配內存)dictionary對象*/
ini = iniparser_load(filename);
if( ini ==NULL)
{
printf("iniparser failure\n");
return -1;
}
sprintf(item,"%s:%s",section,key);
/* 設置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對象(內存)*/
iniparser_freedict(ini);
}
例如我們修改BASIC_INFO節的user的值為yikoupeng,number值為12345
cfg_save_key(PATH,"BASIC_INFO","user","yikoupeng");
cfg_save_key(PATH,"BASIC_INFO","number","12345");
執行結果:

可以看到配置文件basic_info節的user 和number 鍵值對被修改。
后臺回復:iniparser

浙公網安備 33010602011771號