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