我正在尝试创建一个简单的配置文件,它的样子是这样的:
url = http://mysite.com
file = main.exe
true = 0
当程序运行时,我想要它将配置设置加载到下面列出的程序变量中。string url, file;
bool true_false;
我已经做了一些研究,这个链接似乎有所帮助(核子的帖子),但我似乎无法使其正常工作,并且在我的部分上它过于复杂。是否有简单的方法?我可以使用ifstream
来加载文件,但那就是我自己能达到的程度了。谢谢!我正在尝试创建一个简单的配置文件,它的样子是这样的:
url = http://mysite.com
file = main.exe
true = 0
当程序运行时,我想要它将配置设置加载到下面列出的程序变量中。string url, file;
bool true_false;
我已经做了一些研究,这个链接似乎有所帮助(核子的帖子),但我似乎无法使其正常工作,并且在我的部分上它过于复杂。是否有简单的方法?我可以使用ifstream
来加载文件,但那就是我自己能达到的程度了。谢谢!通常,解析这种典型的配置文件最容易分为两个阶段:首先读取行,然后逐行解析。
在C++中,可以使用std::getline()
从流中读取行。默认情况下,它将读取到下一个'\n'
(它会消耗掉但不返回),但是您也可以传递其他定界符,从而使它成为读取到某个字符的良好选择,例如您的示例中的=
。
为简单起见,以下假设=
周围没有空格。如果要在这些位置允许空格,则必须在读取值之前策略性地放置is >> std::ws
,并从键中去除尾随空格。然而,在我看来,对于配置文件阅读器而言,这种语法的稍微增加的灵活性并不值得麻烦。
#include <sstream>
const char config[] = "url=http://example.com\n"
"file=main.exe\n"
"true=0";
std::istringstream is_file(config);
std::string line;
while( std::getline(is_file, line) )
{
std::istringstream is_line(line);
std::string key;
if( std::getline(is_line, key, '=') )
{
std::string value;
if( std::getline(is_line, value) )
store_line(key, value);
}
}
(将错误处理添加作为读者的练习留下。)
正如其他人指出的那样,使用现有的配置文件解析库可能比重新发明轮子更省事。
例如,如果你决定使用Config4Cpp库(我维护),那么你的配置文件语法将会略有不同(在值周围加上双引号,并用分号终止赋值语句),如下面的示例所示:
# File: someFile.cfg
url = "http://mysite.com";
file = "main.exe";
true_false = "true";
#include <config4cpp/Configuration.h>
#include <iostream>
using namespace config4cpp;
using namespace std;
int main(int argc, char ** argv)
{
Configuration * cfg = Configuration::create();
const char * scope = "";
const char * configFile = "someFile.cfg";
const char * url;
const char * file;
bool true_false;
try {
cfg->parse(configFile);
url = cfg->lookupString(scope, "url");
file = cfg->lookupString(scope, "file");
true_false = cfg->lookupBoolean(scope, "true_false");
} catch(const ConfigurationException & ex) {
cerr << ex.c_str() << endl;
cfg->destroy();
return 1;
}
cout << "url=" << url << "; file=" << file
<< "; true_false=" << true_false
<< endl;
cfg->destroy();
return 0;
}
Config4Cpp 网站 提供全面的文档,但仅阅读“入门指南”的第2章和第3章应该已经足够满足您的需求。
libconfig非常容易使用,更好的是,它使用伪JSON符号以提高可读性。
在Ubuntu上安装非常简单:sudo apt-get install libconfig++8-dev
并且链接:-lconfig++
一个天真的方法可能是这样的:
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
std::map<std::string, std::string> options; // global?
void parse(std::istream & cfgfile)
{
for (std::string line; std::getline(cfgfile, line); )
{
std::istringstream iss(line);
std::string id, eq, val;
bool error = false;
if (!(iss >> id))
{
error = true;
}
else if (id[0] == '#')
{
continue;
}
else if (!(iss >> eq >> val >> std::ws) || eq != "=" || iss.get() != EOF)
{
error = true;
}
if (error)
{
// do something appropriate: throw, skip, warn, etc.
}
else
{
options[id] = val;
}
}
}
现在您可以从全局的options
映射中任何地方访问每个选项的值。如果您想要可转换性,您可以将映射类型设置为boost::variant
。
id == '#'
? - sop{"url": "http://mysite dot com",
"file": "main.exe",
"true": 0}
最近我在为我的项目搜索配置解析库,找到了以下这些库:
我在寻找类似于Python模块ConfigParser的东西,发现了这个:https://github.com/jtilly/inih
这是一个仅包含头文件的C++版本的inih。
inih(INI非此地发明)是一个简单的.INI文件解析器,用C语言编写。它只有几页代码,并且被设计成小而简单,因此非常适合嵌入式系统。它也基本兼容Python的ConfigParser风格的.INI文件,包括RFC 822风格的多行语法和name:value条目。
所以我将上面的一些解决方案合并成了自己的,这对我来说更有意义,更直观,而且出错的可能性更小。我使用一个公共的stp::map
来跟踪可能的配置id,并使用一个struct
来跟踪可能的值。下面是代码:
struct{
std::string PlaybackAssisted = "assisted";
std::string Playback = "playback";
std::string Recording = "record";
std::string Normal = "normal";
} mode_def;
std::map<std::string, std::string> settings = {
{"mode", mode_def.Normal},
{"output_log_path", "/home/root/output_data.log"},
{"input_log_path", "/home/root/input_data.log"},
};
void read_config(const std::string & settings_path){
std::ifstream settings_file(settings_path);
std::string line;
if (settings_file.fail()){
LOG_WARN("Config file does not exist. Default options set to:");
for (auto it = settings.begin(); it != settings.end(); it++){
LOG_INFO("%s=%s", it->first.c_str(), it->second.c_str());
}
}
while (std::getline(settings_file, line)){
std::istringstream iss(line);
std::string id, eq, val;
if (std::getline(iss, id, '=')){
if (std::getline(iss, val)){
if (settings.find(id) != settings.end()){
if (val.empty()){
LOG_INFO("Config \"%s\" is empty. Keeping default \"%s\"", id.c_str(), settings[id].c_str());
}
else{
settings[id] = val;
LOG_INFO("Config \"%s\" read as \"%s\"", id.c_str(), settings[id].c_str());
}
}
else{ //Not present in map
LOG_ERROR("Setting \"%s\" not defined, ignoring it", id.c_str());
continue;
}
}
else{
// Comment line, skiping it
continue;
}
}
else{
//Empty line, skipping it
continue;
}
}
}
我正在寻找一个类似的简单C++配置文件解析器,这个教程网站为我提供了一个基本但可行的解决方案。它是一个快速而不太规范的解决方案,可以完成工作。
myConfig.txt
gamma=2.8
mode = 1
path = D:\Photoshop\Projects\Workspace\Images\
#include <iostream>
#include <fstream>
#include <algorithm>
#include <string>
int main()
{
double gamma = 0;
int mode = 0;
std::string path;
// std::ifstream is RAII, i.e. no need to call close
std::ifstream cFile("myConfig.txt");
if (cFile.is_open())
{
std::string line;
while (getline(cFile, line))
{
line.erase(std::remove_if(line.begin(), line.end(), isspace),line.end());
if (line[0] == '#' || line.empty()) continue;
auto delimiterPos = line.find("=");
auto name = line.substr(0, delimiterPos);
auto value = line.substr(delimiterPos + 1);
//Custom coding
if (name == "gamma") gamma = std::stod(value);
else if (name == "mode") mode = std::stoi(value);
else if (name == "path") path = value;
}
}
else
{
std::cerr << "Couldn't open config file for reading.\n";
}
std::cout << "\nGamma=" << gamma;
std::cout << "\nMode=" << mode;
std::cout << "\nPath=" << path;
std::getchar();
}