mINI
v0.9.16
信息
这是一个用于操作INI文件的小型仅有头文件的C++库。
它遵循以下格式:
- 默认情况下,区段和键名称不区分大小写
- 忽略区段、键和值周围的空白字符
- 允许使用空的区段和键名称
- 不属于任何区段的键将被忽略
- 注释是以分号(
;
)作为第一个非空白字符的行 - 在区段行上允许有尾部注释,但在键/值行上不允许
- 每个条目都在单独的一行上,不支持多行
; 注释
[区段]
键 = 值
文件是按需一次性读取的,之后数据保存在内存中并可以进行操作。读写操作后,文件会被关闭。此实用程序支持延迟写入,只写入更改和更新,并保留自定义格式和注释。由write()
调用触发的延迟写入会读取输出文件,找出已经进行的更改,并相应地更新文件。如果你只需要生成文件,请使用generate()
。
读取和写入操作时,区段和键的顺序会被保留。遍历数据时,顺序将与原始文件或添加到结构中的键的顺序相同。
该库使用std::string
类型保存值,并依赖于您的主机环境的编码。它应该能很好地处理UTF-8,但实际效果可能会有所不同。
安装
这是一个头文件库。要安装它,只需将/src/
中的所有内容复制到你自己项目的源代码文件夹中,或使用自定义位置,并确保你的编译器能看到额外的include目录。然后在你的代码中包含该文件:
#include "mini/ini.h"
就这么简单!
基本示例
读取/写入
从名为myfile.ini
的INI文件开始:
; 水果数量
[水果]
苹果=20
橙子=30
我们的代码:
// 首先,创建一个文件实例
mINI::INIFile file("myfile.ini");
// 接下来,创建一个用于保存数据的结构
mINI::INIStructure ini;
// 现在我们可以读取文件
file.read(ini);
// 读取值
std::string& 苹果数量 = ini["水果"]["苹果"];
// 更新一个值
ini["水果"]["橙子"] = "50";
// 添加一个新条目
ini["水果"]["香蕉"] = "100";
// 将更新写入文件
file.write(ini);
运行代码后,我们的INI文件现在看起来像这样:
; 水果数量
[水果]
苹果=20
橙子=50
香蕉=100
生成一个文件
// 创建一个文件实例
mINI::INIFile file("myfile.ini");
// 创建一个数据结构
mINI::INIStructure ini;
// 填充结构
ini["物品"]["椅子"] = "20";
ini["物品"]["气球"] = "100";
// 生成一个INI文件(覆盖任何以前的文件)
file.generate(ini);
操作文件
INIFile
类保持文件名,并公开读取、写入和生成INI文件的函数。它不会保持文件打开,而只是提供一个可以用来访问物理文件的抽象。
创建一个文件实例:
mINI::INIFile file("myfile.ini");
您还需要一个可以操作的结构:
mINI::INIStructure ini;
读取文件:
bool readSuccess = file.read(ini);
在保留注释和自定义格式的情况下写回文件:
bool writeSuccess = file.write(ini);
您可以将第二个参数设置为true
,以便以漂亮的格式写入文件:
bool writeSuccess = file.write(ini, true);
write()
调用将尝试保留原始INI文件的任何自定义格式,并仅为新建的键和区段使用漂亮的格式。
生成一个文件:
file.generate(ini);
请注意,generate()
将覆盖原始文件中的任何自定义格式和注释!
您也可以将漂亮的格式与generate()
一起使用:
file.generate(ini, true);
不带漂亮格式的生成INI文件示例输出:
[section1]
key1=value1
key2=value2
[section2]
key1=value1
带有漂亮格式的生成INI文件示例输出:
[section1]
key1 = value1
key2 = value2
[section2]
key1 = value1
操作数据
读取数据
有两种方式可以从INI结构中读取数据。您可以使用[]
运算符或get()
函数:
// 读取值 - 如果键或区段不存在,它们将被创建
// 返回对真实值的引用
std::string& 值 = ini["区段"]["键"];
// 安全地读取值 - 如果键或区段不存在,它们将不会被创建
// 返回值的副本
std::string 值 = ini.get("区段").get("键");
[]
和get()
操作的区别在于,[]
返回对真实数据的引用(您可以修改),并在不存在时自动创建新项,而get()
返回数据的副本,不会在结构中创建新项。如果您希望避免改变结构,请在使用[]
之前使用has()
。
您可以结合使用[]
和get()
。
章节名称和键名称不区分大小写,并且会删除前后的空白字符。ini["section"]
与ini["SECTION"]
、ini[" sEcTiOn "]
等价。生成的文件始终使用小写表示章节名称和键名称。写入现有文件时,如果章节名称或键名称已存在,将保留原文件中的大小写。
更新数据
设置或更新值的方式如下:
ini["section"]["key"] = "value";
注意,写入文件时,值会被删除前后的空白字符。例如,以下值在从文件中读取时将被转换为"c"
:
ini["a"]["b"] = " c ";
你可以使用set()
一次设置多个值:
ini["section"].set({
{"key1", "value1"},
{"key2", "value2"}
});
创建一个空章节:
ini["section"];
类似地,创建一个空键:
ini["section"]["key"];
删除某个章节的单个键:
bool removeSuccess = ini["section"].remove("key");
删除整个章节:
bool removeSuccess = ini.remove("section");
删除某个章节的所有键:
ini["section"].clear();
删除结构中的所有数据:
ini.clear();
其他功能
检查是否存在某个章节:
bool hasSection = ini.has("section");
检查某个章节中是否存在某个键:
bool hasKey = ini["section"].has("key");
获取某个章节中键的数量:
size_t n_keys = ini["section"].size();
获取结构中章节的数量:
size_t n_sections = ini.size();
注意事项
请注意,[]
操作符将始终创建一个新项,如果该项不存在的话!你可以使用has()
检查项是否存在,然后再执行进一步操作。记住,get()
将返回数据的副本,所以你不应该使用它来执行删除或更新操作!
在大多数现实场景中,使用[]
操作符并不会有问题,因为你通常会查找已知的键,并且可能不关心是否创建了空键或章节。但是,如果你有一个场景不希望向结构添加新项,要么使用get()
来获取项,要么在使用[]
操作符之前使用has()
检查项是否存在,以确保安全操作。
下面是一个安全操作数据的示例:
if (ini.has("section"))
{
// 我们有这个章节,可以安全访问它而不创建新章节
auto& collection = ini["section"];
if (collection.has("key"))
{
// 我们有这个键,可以安全访问它而不创建新键
auto& value = collection["key"];
}
}
遍历
你可以按插入顺序遍历整个结构。下面的示例循环遍历结构并以熟悉的格式显示结果:
for (auto const& it : ini)
{
auto const& section = it.first;
auto const& collection = it.second;
std::cout << "[" << section << "]" << std::endl;
for (auto const& it2 : collection)
{
auto const& key = it2.first;
auto const& value = it2.second;
std::cout << key << "=" << value << std::endl;
}
}
it.first
类型始终为std::string
。
it.second
是一个对象,在第一层为mINI::INIMap
类型,在第二层为std::string
类型。
API只公开了const_iterator
,所以你不能直接使用迭代器来修改数据。但是,你可以在遍历时正常访问该结构:
// 将结构中所有值更改为"banana"
for (auto const& it : ini)
{
auto const& section = it.first;
auto const& collection = it.second;
for (auto const& it2 : collection)
{
auto const& key = it2.first;
ini[section][key] = "banana"; // O(1)因为使用哈希表
}
}
区分大小写
如果你希望该库不忽略大小写,请在包含库之前添加指令#define MINI_CASE_SENSITIVE
:
#define MINI_CASE_SENSITIVE
#include "mini/ini.h"
这将影响从文件读取和写入,以及对结构的访问。
鸣谢
- lest - 测试框架
许可证
版权所有 © 2018 Danijel Durakovic
根据MIT许可证的条款获得许可