在嵌入式或系統級開發中,JSON、XML 等重型格式往往過於龐大。本文展示如何從零開始,設計一個支援「`$key = "value"`」語法的極簡配置檔解析器,並將其封裝為可複用的 C 庫,提供 `sc_start()` 初始化與 `sc_get(key)` 取值兩大介面。
$heoo = "Hello World\n";
$yexu = "\"你好嗎\", 我很好.謝謝!";
$end = "coding future 123 runing, ";
#ifndef _H_SCCONF
#define _H_SCCONF
#define _STR_SCCONF_PATH "module/schead/config/config.ini"
extern void sc_start(void); /* 初始化/重載 */
extern const char* sc_get(const char* key); /* 取值,失敗回傳 NULL */
#endif
struct dict {
_TREE_HEAD; /* 通用樹節點頭部 */
char* key;
char* value;
};
採用通用 `tree.h` 實現鍵值存儲,支援 O(log n) 查找。記憶體管理由 `tstring.h` 動態字串輔助,執行緒安全由 `scatom.h` 原子鎖保護。
static void __analysis_start(FILE* txt, tree_t* proot) {
char c, n;
TSTRING_CREATE(tstr); /* 動態字串建構器 */
while ((c = fgetc(txt)) != EOF) {
/* 1. 跳過空白 */
while (c != EOF && isspace(c)) c = fgetc(txt);
/* 2. 必須以 $ 開頭 */
if (c != '$') { while (c != EOF && c != '\n') c = fgetc(txt); continue; }
if ((c = fgetc(txt)) != EOF && isspace(c)) { while (c != EOF && c != '\n') c = fgetc(txt); continue; }
tstr.len = 0;
/* 3. 讀取 key 直到 = */
while (c != EOF && c != '=') { if (!isspace(c)) tstring_append(&tstr, c); c = fgetc(txt); }
if (c != '=') break;
c = '\0'; /* key 結束標記 */
/* 4. 跳過空白找起始 " */
while (c != EOF && c != '"') { if (!isspace(c)) tstring_append(&tstr, c); c = fgetc(txt); }
if (c != '"') break;
/* 5. 讀取 value 直到未轉義的 " */
for (n = c; (c = fgetc(txt)) != EOF; n = c) {
if (c == '"' && n != '\\') break;
tstring_append(&tstr, c);
}
if (c != '"') break;
tree_add(proot, &tstr); /* 將 key\0value 串存入樹 */
/* 6. 讀完行尾 */
while (c != EOF && c != '\n') c = fgetc(txt);
if (c != '\n') break;
}
TSTRING_DESTROY(tstr);
}
void sc_start(void) {
FILE* txt = fopen(_STR_SCCONF_PATH, "r");
if (!txt) { SL_NOTICE("fopen config error"); return; }
ATOM_LOCK(__lock);
__tds_destroy(); /* 釋放舊樹 */
__tds = tree_create(__dict_new, __dict_acmp, __dict_gdcmp, __dict_del);
__analysis_start(txt, &__tds);
ATOM_UNLOCK(__lock);
fclose(txt);
}
inline const char* sc_get(const char* key) {
struct dict* kv;
if ((!key) || (!*key) || !(kv = tree_get(__tds, key, NULL))) return NULL;
return kv->value;
}
#include
#include
int main() {
sc_start();
puts(sc_get("heoo")); /* Hello World */
puts(sc_get("yexu")); /* "你好嗎", 我很好.謝謝! */
puts(sc_get("end")); /* coding future 123 runing, */
puts(sc_get("heoo2") ? : "heoo2不存在");
return 0;
}

本庫展示了「語法解析 + 字典資料結構 + 記憶體管理 + 執行緒安全」的完整工程化流程。後續可擴充支援:區塊、陣列、型別自動推導、熱重載通知等特性,逐步演進為生產級配置中心客戶端。