以下是一个完整的 **C语言配置文件解析模块**,支持:
- **INI 格式**(如 `hostapd.conf`):支持 `[General]`、`[BSS]` 等 section。
- **WPA_SUPPLICANT 格式**(如 `wpa_supplicant.conf`):支持 `network={...}` 结构。
- **多值配置项**(如 `key=val1 val2`)。
- **section_id 支持**:区分多个 `network` 段。
- **通用 API 接口**:支持增删改查。
---
## 📁 文件结构
```
config_parser/
├── config_parser.h
├── config_parser.c
└── main.c
```
---
## 📄 1. `config_parser.h`
```c
#ifndef CONFIG_PARSER_H
#define CONFIG_PARSER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX_KEY_LEN 256
#define MAX_VAL_LEN 1024
#define MAX_VALUES 32
#define MAX_SECTION_LEN 256
// 多值配置项
typedef struct config_item {
char key[MAX_KEY_LEN];
char values[MAX_VALUES][MAX_VAL_LEN];
int value_count;
} ConfigItem;
// 支持嵌套结构(如 network={...})
typedef struct config_section {
char name[MAX_SECTION_LEN]; // 段名,如 "network"
char section_id[MAX_VAL_LEN]; // 段标识,如 ssid="my_ssid"
ConfigItem **items;
int item_count;
} ConfigSection;
// 整个配置
typedef struct {
ConfigSection **sections;
int section_count;
} Config;
// 创建和释放
Config *config_new();
void config_free(Config *config);
// hostapd.conf 读写(INI 风格)
int config_read(Config *config, const char *filename);
int config_write(Config *config, const char *filename);
// wpa_supplicant.conf 读写(network={...} 风格)
int config_read_wpa(Config *config, const char *filename);
int config_write_wpa(Config *config, const char *filename);
// 获取某个 key 的值(支持 BSS 的 bss=xxx)
int config_get_value(Config *config, const char *section, const char *bss, const char *key, char *value, size_t len);
// 设置某个 key 的值(不存在则新增)
int config_set_value(Config *config, const char *section, const char *bss, const char *key, const char *value);
// 删除某个 key(支持 BSS)
int config_del_key(Config *config, const char *section, const char *bss, const char *key);
// 删除某个 section(支持 BSS)
int config_del_section(Config *config, const char *section, const char *bss);
// 新增 section(支持 BSS)
int config_add_section(Config *config, const char *section, const char *bss);
#endif // CONFIG_PARSER_H
```
---
## 📄 2. `config_parser.c`
```c
#include "config_parser.h"
// 工具函数:去除字符串两端空格
void trim(char *str) {
char *end;
while (isspace((unsigned char)*str)) str++;
if (*str == 0) {
*str = '\0';
return;
}
end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
}
// 创建配置
Config *config_new() {
return calloc(1, sizeof(Config));
}
// 释放配置
void config_free(Config *config) {
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
for (int j = 0; j < sec->item_count; j++)
free(sec->items[j]);
free(sec->items);
free(sec);
}
free(config->sections);
free(config);
}
// 查找 section(支持 bss)
ConfigSection *find_section(Config *config, const char *section_name, const char *bss_name) {
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
if (strcmp(sec->name, section_name) == 0) {
if (strcmp(section_name, "BSS") == 0) {
if (bss_name && strcmp(sec->bss_name, bss_name) == 0)
return sec;
} else {
return sec;
}
}
}
return NULL;
}
// 获取某个 key 的值
int config_get_value(Config *config, const char *section, const char *bss, const char *key, char *value, size_t len) {
ConfigSection *sec = find_section(config, section, bss);
if (!sec) return -1;
for (int i = 0; i < sec->item_count; i++) {
if (strcmp(sec->items[i]->key, key) == 0) {
strncpy(value, sec->items[i]->values[0], len);
return 0;
}
}
return -1;
}
// 设置某个 key 的值(不存在则新增)
int config_set_value(Config *config, const char *section, const char *bss, const char *key, const char *value) {
ConfigSection *sec = find_section(config, section, bss);
if (!sec) {
config_add_section(config, section, bss);
sec = find_section(config, section, bss);
}
for (int i = 0; i < sec->item_count; i++) {
if (strcmp(sec->items[i]->key, key) == 0) {
char *token = strtok((char *)value, " ");
int count = 0;
while (token && count < MAX_VALUES) {
strncpy(sec->items[i]->values[count++], token, MAX_VAL_LEN - 1);
token = strtok(NULL, " ");
}
sec->items[i]->value_count = count;
return 0;
}
}
ConfigItem *new_item = calloc(1, sizeof(ConfigItem));
strncpy(new_item->key, key, MAX_KEY_LEN - 1);
char *token = strtok((char *)value, " ");
int count = 0;
while (token && count < MAX_VALUES) {
strncpy(new_item->values[count++], token, MAX_VAL_LEN - 1);
token = strtok(NULL, " ");
}
new_item->value_count = count;
sec->items = realloc(sec->items, (sec->item_count + 1) * sizeof(ConfigItem *));
sec->items[sec->item_count++] = new_item;
return 0;
}
// 删除某个 key(支持 BSS)
int config_del_key(Config *config, const char *section, const char *bss, const char *key) {
ConfigSection *sec = find_section(config, section, bss);
if (!sec) return -1;
for (int i = 0; i < sec->item_count; i++) {
if (strcmp(sec->items[i]->key, key) == 0) {
free(sec->items[i]);
for (int j = i; j < sec->item_count - 1; j++)
sec->items[j] = sec->items[j + 1];
sec->item_count--;
return 0;
}
}
return -1;
}
// 删除某个 section(支持 BSS)
int config_del_section(Config *config, const char *section, const char *bss) {
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
if (strcmp(sec->name, section) == 0) {
if (strcmp(section, "BSS") == 0) {
if (bss && strcmp(sec->bss_name, bss) == 0) {
for (int j = 0; j < sec->item_count; j++)
free(sec->items[j]);
free(sec->items);
free(sec);
for (int j = i; j < config->section_count - 1; j++)
config->sections[j] = config->sections[j + 1];
config->section_count--;
return 0;
}
} else {
for (int j = 0; j < sec->item_count; j++)
free(sec->items[j]);
free(sec->items);
free(sec);
for (int j = i; j < config->section_count - 1; j++)
config->sections[j] = config->sections[j + 1];
config->section_count--;
return 0;
}
}
}
return -1;
}
// 新增 section(支持 BSS)
int config_add_section(Config *config, const char *section, const char *bss) {
ConfigSection *new_sec = calloc(1, sizeof(ConfigSection));
strncpy(new_sec->name, section, MAX_SECTION_LEN - 1);
if (strcmp(section, "BSS") == 0 && bss)
strncpy(new_sec->bss_name, bss, MAX_VAL_LEN - 1);
new_sec->items = NULL;
new_sec->item_count = 0;
config->sections = realloc(config->sections, (config->section_count + 1) * sizeof(ConfigSection *));
config->sections[config->section_count++] = new_sec;
return 0;
}
// 读取 INI 格式配置文件(hostapd.conf)
int config_read(Config *config, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
char line[1024];
ConfigSection *current_section = NULL;
while (fgets(line, sizeof(line), fp)) {
char *comment = strchr(line, '#');
if (comment) *comment = '\0';
char *end = line + strlen(line) - 1;
while (end >= line && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
if (strlen(line) == 0) continue;
if (line[0] == '[') {
char *end_bracket = strchr(line, ']');
if (end_bracket) {
*end_bracket = '\0';
char *name = line + 1;
current_section = find_section(config, name, NULL);
if (!current_section) {
config_add_section(config, name, NULL);
current_section = find_section(config, name, NULL);
}
}
} else {
char key[MAX_KEY_LEN], val[MAX_VAL_LEN];
if (sscanf(line, "%[^=]=%s", key, val) == 2) {
trim(key);
trim(val);
if (current_section) {
config_set_value(config, current_section->name, current_section->bss_name, key, val);
} else {
config_set_value(config, "General", NULL, key, val);
}
}
}
}
fclose(fp);
return 0;
}
// 写入 INI 格式配置文件
int config_write(Config *config, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
if (strcmp(sec->name, "") != 0) {
if (strcmp(sec->name, "BSS") == 0 && sec->bss_name[0] != '\0') {
fprintf(fp, "[%s]\n", sec->name);
fprintf(fp, "bss=%s\n", sec->bss_name);
} else {
fprintf(fp, "[%s]\n", sec->name);
}
}
for (int j = 0; j < sec->item_count; j++) {
ConfigItem *item = sec->items[j];
fprintf(fp, "%s=", item->key);
for (int k = 0; k < item->value_count; k++) {
fprintf(fp, "%s ", item->values[k]);
}
fprintf(fp, "\n");
}
fprintf(fp, "\n");
}
fclose(fp);
return 0;
}
// 读取 WPA_SUPPLICANT 格式配置文件(network={...})
int config_read_wpa(Config *config, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
char line[1024];
ConfigSection *current_section = NULL;
int in_section = 0;
while (fgets(line, sizeof(line), fp)) {
char *comment = strchr(line, '#');
if (comment) *comment = '\0';
char *end = line + strlen(line) - 1;
while (end >= line && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
if (strlen(line) == 0) continue;
if (strncmp(line, "network={", 9) == 0) {
current_section = calloc(1, sizeof(ConfigSection));
strncpy(current_section->name, "network", MAX_SECTION_LEN - 1);
current_section->items = NULL;
current_section->item_count = 0;
config->sections = realloc(config->sections, (config->section_count + 1) * sizeof(ConfigSection *));
config->sections[config->section_count++] = current_section;
in_section = 1;
continue;
}
if (in_section && strcmp(line, "}") == 0) {
in_section = 0;
// 尝试从当前 section 中找 ssid,用于 section_id
for (int i = 0; i < current_section->item_count; i++) {
if (strcmp(current_section->items[i]->key, "ssid") == 0) {
strncpy(current_section->section_id, current_section->items[i]->values[0],
sizeof(current_section->section_id));
break;
}
}
current_section = NULL;
continue;
}
if (in_section) {
char key[MAX_KEY_LEN], val[MAX_VAL_LEN];
if (sscanf(line, "%[^=]=%s", key, val) == 2) {
trim(key);
trim(val);
ConfigItem *new_item = calloc(1, sizeof(ConfigItem));
strncpy(new_item->key, key, MAX_KEY_LEN - 1);
char *token = strtok(val, " ");
int count = 0;
while (token && count < MAX_VALUES) {
strncpy(new_item->values[count++], token, MAX_VAL_LEN - 1);
token = strtok(NULL, " ");
}
new_item->value_count = count;
current_section->items = realloc(current_section->items,
(current_section->item_count + 1) * sizeof(ConfigItem *));
current_section->items[current_section->item_count++] = new_item;
}
}
}
fclose(fp);
return 0;
}
// 写入 WPA_SUPPLICANT 格式配置文件
int config_write_wpa(Config *config, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
if (strcmp(sec->name, "network") == 0) {
fprintf(fp, "network={\n");
for (int j = 0; j < sec->item_count; j++) {
ConfigItem *item = sec->items[j];
fprintf(fp, " %s=", item->key);
for (int k = 0; k < item->value_count; k++) {
fprintf(fp, "%s ", item->values[k]);
}
fprintf(fp, "\n");
}
fprintf(fp, "}\n\n");
}
}
fclose(fp);
return 0;
}
```
---
## 📄 3. `main.c` 测试文件
```c
#include "config_parser.h"
#include <stdio.h>
int main() {
Config *config = config_new();
// 读取 hostapd.conf
if (config_read(config, "hostapd.conf") == 0) {
char value[1024];
config_get_value(config, "BSS", "wlan0.1", "ssid", value, sizeof(value));
printf("SSID: %s\n", value);
config_set_value(config, "BSS", "wlan0.1", "ssid", "NewHome1");
config_add_section(config, "BSS", "wlan0.3");
config_set_value(config, "BSS", "wlan0.3", "ssid", "Guest");
config_write(config, "new_hostapd.conf");
config_free(config);
}
config = config_new();
// 读取 wpa_supplicant.conf
if (config_read_wpa(config, "wpa_supplicant.conf") == 0) {
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
if (strcmp(sec->name, "network") == 0 &&
strcmp(sec->section_id, "\"my_ssid\"") == 0) {
for (int j = 0; j < sec->item_count; j++) {
if (strcmp(sec->items[j]->key, "psk") == 0) {
strncpy(sec->items[j]->values[0], "\"new_password\"", sizeof(sec->items[j]->values[0]));
sec->items[j]->value_count = 1;
break;
}
}
break;
}
}
config_write_wpa(config, "new_wpa.conf");
config_free(config);
}
return 0;
}
```
---
## ✅ 编译命令(Linux)
```bash
gcc -o config_parser main.c config_parser.c
```
---其中nt config_write(Config *config, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
for (int i = 0; i < config->section_count; i++) {
ConfigSection *sec = config->sections[i];
if (strcmp(sec->name, "") != 0) {
if (strcmp(sec->name, "BSS") == 0 && sec->section_id[0] != '\0') {
fprintf(fp, "[%s]\n", sec->name);
fprintf(fp, "bss=%s\n", sec->bss_name);
} else {
fprintf(fp, "[%s]\n", sec->name);
}
}
for (int j = 0; j < sec->item_count; j++) {
ConfigItem *item = sec->items[j];
fprintf(fp, "%s=", item->key);
for (int k = 0; k < item->value_count; k++) {
fprintf(fp, "%s ", item->values[k]);
}
fprintf(fp, "\n");
}
fprintf(fp, "\n");
}
fclose(fp);
return 0;
}并没有sec->bss_name)定义