用 C++ 创建一个简单的配置文件和解析器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6892754/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a simple configuration file and parser in C++
提问by llk
I am trying to create a simple configuration file that looks like this
我正在尝试创建一个看起来像这样的简单配置文件
url = http://mysite.com
file = main.exe
true = 0
when the program runs, I would like it to load the configuration settings into the programs variables listed below.
当程序运行时,我希望它将配置设置加载到下面列出的程序变量中。
string url, file;
bool true_false;
I have done some research and thislink seemed to help (nucleon's post) but I can't seem to get it to work and it is too complicated to understand on my part. Is there a simple way of doing this? I can load the file using ifstream
but that is as far as I can get on my own. Thanks!
我做了一些研究,这个链接似乎有帮助(nucleon 的帖子),但我似乎无法让它工作,而且它太复杂了,我无法理解。有没有一种简单的方法可以做到这一点?我可以使用加载文件,ifstream
但这是我自己所能得到的。谢谢!
采纳答案by sbi
In general, it's easiest to parse such typical config files in two stages: first read the lines, and then parse those one by one.
In C++, lines can be read from a stream using std::getline()
. While by default it will read up to the next '\n'
(which it will consume, but not return), you can pass it some other delimiter, too, which makes it a good candidate for reading up-to-some-char, like =
in your example.
一般来说,最容易分两个阶段解析此类典型配置文件:首先读取行,然后逐行解析。
在 C++ 中,可以使用std::getline()
. 虽然默认情况下它会读到下一个'\n'
(它会消耗,但不会返回),你也可以给它传递一些其他的分隔符,这使它成为读取最多一些字符的一个很好的候选者,就像=
在你的例子。
For simplicity, the following presumes that the =
are notsurrounded by whitespace. If you want to allow whitespaces at these positions, you will have to strategically place is >> std::ws
before reading the value and remove trailing whitespaces from the keys. However, IMO the little added flexibility in the syntax is not worth the hassle for a config file reader.
为简单起见,下面假定的=
是未用空白包围。如果您想在这些位置允许空格,则必须is >> std::ws
在读取值之前有策略地放置并从键中删除尾随空格。但是,对于配置文件阅读器来说,IMO 在语法上增加的一点灵活性并不值得。
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);
}
}
(Adding error handling is left as an exercise to the reader.)
(添加错误处理留给读者作为练习。)
回答by Ciaran McHale
As others have pointed out, it will probably be less work to make use of an existing configuration-file parser library rather than re-invent the wheel.
正如其他人指出的那样,使用现有的配置文件解析器库而不是重新发明轮子可能会减少工作量。
For example, if you decide to use the Config4Cpplibrary (which I maintain), then your configuration file syntax will be slightly different (put double quotes around values and terminate assignment statements with a semicolon) as shown in the example below:
例如,如果您决定使用Config4Cpp库(由我维护),那么您的配置文件语法将略有不同(在值周围加上双引号并用分号终止赋值语句),如下例所示:
# File: someFile.cfg
url = "http://mysite.com";
file = "main.exe";
true_false = "true";
The following program parses the above configuration file, copies the desired values into variables and prints them:
以下程序解析上述配置文件,将所需值复制到变量中并打印出来:
#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;
}
The Config4Cpp websiteprovides comprehensive documentation, but reading just Chapters 2 and 3 of the "Getting Started Guide" should be more than sufficient for your needs.
Config4Cpp网站提供了全面的文档,但仅阅读“入门指南”的第 2 章和第 3 章就足以满足您的需要。
回答by Ciaran McHale
回答by Kerrek SB
A naive approach could look like this:
一种天真的方法可能如下所示:
#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;
}
}
}
Now you can access each option value from the global options
map anywhere in your program. If you want castability, you could make the mapped type a boost::variant
.
现在,您可以options
在程序的任何位置访问全局映射中的每个选项值。如果您想要可铸造性,您可以将映射类型设为 a boost::variant
。
回答by yosh kemu
Why not trying something simple and human-readable, like JSON (or XML) ?
为什么不尝试一些简单易读的东西,比如 JSON(或 XML)?
There are many pre-made open-source implementations of JSON (or XML) for C++ - I would use one of them.
有许多用于 C++ 的 JSON(或 XML)的预制开源实现 - 我会使用其中之一。
And if you want something more "binary" - try BJSON or BSON :)
如果你想要更“二进制”的东西 - 试试 BJSON 或 BSON :)
回答by Ivan Samygin
I've searched config parsing libraries for my project recently and found these libraries:
我最近为我的项目搜索了配置解析库,并找到了这些库:
- http://www.hyperrealm.com/libconfig/- small but powerfull and easy-to-use library, examples of using are in the source package and here
- http://pocoproject.org/docs/Poco.Util.PropertyFileConfiguration.html- a part of pocoproject. You can use abstraction in your code and behind that use various supported implementations (ini files, Xml configs, even environment variables)
- http://www.hyperrealm.com/libconfig/- 小而强大且易于使用的库,使用示例在源包中和这里
- http://pocoproject.org/docs/Poco.Util.PropertyFileConfiguration.html- pocoproject的一部分。您可以在代码中使用抽象,并在其背后使用各种支持的实现(ini 文件、Xml 配置,甚至环境变量)
回答by patmanpato
How about formatting your configuration as JSON, and using a library like jsoncpp?
如何将您的配置格式化为 JSON,并使用像jsoncpp这样的库?
e.g.
例如
{"url": "http://mysite dot com",
"file": "main.exe",
"true": 0}
You can then read it into named variables, or even store it all in a std::map, etc. The latter means you can add options without having to change and recompile your configuration parser.
然后您可以将其读入命名变量,甚至将其全部存储在 std::map 等中。后者意味着您可以添加选项而无需更改和重新编译您的配置解析器。
回答by ams
I was looking for something that worked like the python module ConfigParserand found this: https://github.com/jtilly/inih
我正在寻找像 python 模块ConfigParser一样工作的东西,发现了这个:https: //github.com/jtilly/inih
This is a header only C++ version of inih.
inih (INI Not Invented Here) is a simple .INI file parser written in C. It's only a couple of pages of code, and it was designed to be small and simple, so it's good for embedded systems. It's also more or less compatible with Python's ConfigParser style of .INI files, including RFC 822-style multi-line syntax and name: value entries.
这是 inih 的只有头文件的 C++ 版本。
inih (INI Not Invented Here) 是一个用 C 语言编写的简单的 .INI 文件解析器。它只有几页代码,而且它被设计得小巧而简单,因此非常适合嵌入式系统。它也或多或少地与 Python 的 ConfigParser 样式的 .INI 文件兼容,包括 RFC 822 样式的多行语法和名称:值条目。
回答by haripkannan
Here is a simple work around for white space between the '=' sign and the data, in the config file. Assign to the istringstream from the location after the '=' sign and when reading from it, any leading white space is ignored.
这是配置文件中“=”符号和数据之间的空白区域的简单解决方法。从 '=' 符号之后的位置分配给 istringstream,从它读取时,任何前导空格都将被忽略。
Note: while using an istringstream in a loop, make sure you call clear() before assigning a new string to it.
注意:在循环中使用 istringstream 时,请确保在为其分配新字符串之前调用 clear()。
//config.txt
//Input name = image1.png
//Num. of rows = 100
//Num. of cols = 150
std::string ipName;
int nR, nC;
std::ifstream fin("config.txt");
std::string line;
std::istringstream sin;
while (std::getline(fin, line)) {
sin.str(line.substr(line.find("=")+1));
if (line.find("Input name") != std::string::npos) {
std::cout<<"Input name "<<sin.str()<<std::endl;
sin >> ipName;
}
else if (line.find("Num. of rows") != std::string::npos) {
sin >> nR;
}
else if (line.find("Num. of cols") != std::string::npos) {
sin >> nC;
}
sin.clear();
}
回答by fbarber
SimpleConfigFileis a library that does exactly what you require and it is very simple to use.
SimpleConfigFile是一个完全符合您要求的库,并且使用起来非常简单。
# File file.cfg
url = http://example.com
file = main.exe
true = 0
The following program reads the previous configuration file:
以下程序读取之前的配置文件:
#include<iostream>
#include<string>
#include<vector>
#include "config_file.h"
int main(void)
{
// Variables that we want to read from the config file
std::string url, file;
bool true_false;
// Names for the variables in the config file. They can be different from the actual variable names.
std::vector<std::string> ln = {"url","file","true"};
// Open the config file for reading
std::ifstream f_in("file.cfg");
CFG::ReadFile(f_in, ln, url, file, true_false);
f_in.close();
std::cout << "url: " << url << std::endl;
std::cout << "file: " << file << std::endl;
std::cout << "true: " << true_false << std::endl;
return 0;
}
The function CFG::ReadFile
uses variadic templates. This way, you can pass the variables you want to read and the corresponding type is used for reading the data in the appropriate way.
该函数CFG::ReadFile
使用可变参数模板。这样,你就可以传递你想要读取的变量,并使用相应的类型以适当的方式读取数据。