跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C++如何解析INI配置文件 _ 字符串分割与map存储实现【实战】

INI文件解析核心是状态机管理节名、键归属与引号包裹状态,需跳过注释空行,按首字符分类处理;安全分割键值对须手动扫描并忽略引号内等号。 INI文件解析的核心难点不是分割,而是节(section)和键值对的上下文管理 直接用
std::string::find
std::getline
=
[
粗暴切分,会漏掉注释、忽略空行、混淆嵌套等号(如
path=C:\test\file.ini
)、把带空格的值截断。真正要处理的是「当前在哪一节」「该键属于哪个节」「值是否被引号包裹」这三个状态。 建议用单次遍历 + 状态机方式处理,维护:
current_section
std::string
)、
config
std::map<:string std::map std::string>>
),逐行读入后按首字符分类: 以
;
#
开头 → 跳过整行 以
[
开头且以
]
结尾 → 提取节名,赋给
current_section
,注意 trim 两端空白 含
=
且不在引号内 → 拆分左右,左为键(trim),右为值(保留右侧所有字符,再 trim 左右空格,但不删内部空格) 其他行 → 忽略(避免误判无等号的非法行) 如何安全地按
=
分割键值,又不破坏路径或含等号的值 不能用
str.find('=')
取第一个等号就切——因为
url=https://example.com?a=1&b=2
是合法值。必须跳过引号内的等号。一个轻量做法是手动扫描:从左到右,用布尔标记是否在双引号内(遇到
"
就翻转),只在非引号区域响应第一个
=
。 示例逻辑片段: 立即学习 “ C++免费学习笔记(深入) ”;
size_t pos = 0; bool in_quote = false; for (size_t i = 0; i < line.length(); ++i) { if (line[i] == '"') in_quote = !in_quote; else if (line[i] == '=' && !in_quote) { pos = i; break; } } if (pos == 0) continue; // 未找到有效等号 std::string key = trim(line.substr(0, pos)); std::string value = trim(line.substr(pos + 1));
注意:
trim()
需自行实现,跳过
' '
'\t'
,但不要删
'\r'
(Windows 行尾),否则影响后续判断。
std::map
嵌套存储的性能与查找代价你得心里有数 用
std::map<:string std::map std::string>>
查找某个键时,要两次
find()
:先查节,再查键。平均复杂度 O(log N + log M),N 是节总数,M 是该节键数。对几百节、几千键的配置,没问题;但若频繁查同一节下的多个键,可缓存节 map 的迭代器,避免重复查找
config.find(section)
。 更关键的是内存布局:每个
std::map
是红黑树,节点动态分配,缓存不友好。如果配置静态、读多写少,考虑改用
std::unordered_map
(哈希表),但需注意:C++11 起
std::unordered_map<:string ...>
已支持,无需自定义哈希函数。 C函数速查手册(CHM版) C函数速查手册(CHM版) 下载 替换写法:
std::unordered_map> config;
优势是平均 O(1) 查节 + O(1) 查键,但最坏退化为 O(N),且不保证插入顺序——INI 原始顺序丢失,这点不影响功能,但调试时可能困惑。 Windows 下读取 INI 时换行符和 BOM 容易导致解析失败 用
std::ifstream
默认以文本模式打开,
\r\n
会被转成
\n
,看似省心,但若文件含 UTF-8 BOM(
EF BB BF
),
std::getline
会把 BOM 当作行首字符,导致第一节名变成
"\xEF\xBB\xBFSectionName"
,后续所有查找失效。 解决方法只有两个: 打开文件前,用二进制模式读前 3 字节判断 BOM,若有则跳过; 更简单:统一用
std::wifstream
+
std::locale
绑定 UTF-8 facet(C++11 起支持),或直接用第三方库如
utf8cpp
做预处理。 实践中,多数项目接受「要求 配置文件 保存为 UTF-8 without BOM」,并在文档里写明——比代码里硬扛 BOM 更可靠。 另外,
std::getline
读到
\r\n
时,
line
不含
\r
,但若某行末尾意外混入
\r
(比如 Mac 旧格式),
trim()
必须包含
'\r'
,否则键名末尾会多一个不可见字符,导致查找失败。

相关文章