用于解析类似windows下ini配置文件。
INI配置文件有三要素parameters,sections和comments。
- parameters
指一条配置,就像key = value这样的。 - sections
sections是parameters的集合,sections必须独占一行并且用[]括起来。
sections没有明显的结束方式,一个sections的开始就是另一个sections的结束。 - comments
指INI配置文件的注释,以 ; 开头。
例如:
; default config
[DEFAULT]
admin_http_listen = 127.0.0.1:80
admin_https_listen = 127.0.0.1:443 ;ssl
通常在windows下使用GetPrivateProfileString、GetPrivateProfileInt等方式获取上述ini配置文件。在linux下就不适用,这里给出一种c++方案。
1、定义一个variant结构
定义一个variant结构,用于保存params的不同类型键值,例如字符串、整形、浮点等。
// handy variant class as key/values
struct variant : public std::string {
template<typename T>
variant(const T &t) : std::string(std::to_string(t))
{ }
//template<size_t N>
//variant(const char(&s)[N]) :
// std::string(s, N)
//{ }
variant(const char *cstr): std::string(cstr) { }
variant(const std::string &other = {}) : std::string(other) { }
template<typename T>
operator T() const
{
T t;
std::stringstream ss;
return (ss << *this && ss >> t) ? t : T();
}
template<typename T> bool operator ==(const T &t) const
{
return 0 == this->compare(variant(t));
}
bool operator ==(const char *t) const
{
return this->compare(t) == 0;
}
//template<typename T>
//T as() const
//{
// return (T)(*this);
//}
};
2、实现字符串的trim函数
// string util
namespace Util {
//去除前后的空格、回车符、制表符
std::string& trim(std::string &s, const std::string &chars = "\t ")
{
// TRIM(s, chars);
std::string map(0xFF, '\0');
for(auto &ch : chars) {
map[(unsigned char &)ch] = '\1';
}
while(s.size() && map.at((unsigned char &)s.back())) s.pop_back();
while(s.size() && map.at((unsigned char &)s.front())) s.erase(0, 1);
return s;
}
};
3、ConfigureParser类
// config
#include <fstream>
template<typename key, typename variant>
class ConfigParser_basic : public std::map<key, std::map<key, variant>>
{
#if defined(WIN32) || defined(_WIN32)
const std::string delim = "\r\n";
#elif
const std::string delim = "\n";
#endif
public:
bool parse(const std::string &text)
{
// reset, split lines and parse
std::vector<std::string> lines = tokenize(text, delim);
std::string tag, key;
for(auto &line : lines) {
// trim blanks
line = Util::trim(line);
// split line into tokens and parse tokens
if(line.empty() || line.front() == ';' || line.front() == '#') {
continue;
}
if(line.size() >= 3 && line.front() == '[' && line.back() == ']') {
tag = Util::trim(line.substr(1, line.size() - 2));
}
else {
auto at = line.find('=');
key = Util::trim(line.substr(0, at));
std::string value = line.substr(at + 1);
std::string::size_type spos = value.find_first_of(";#");
if(std::string::npos != spos)
value.erase(spos, value.size() - spos);
(*this)[tag][key] = (at == std::string::npos ? std::string() : Util::trim(value));
}
}
return true;
}
bool parseFile(const std::string &fileName)
{
std::ifstream in(fileName, std::ios::in | std::ios::binary | std::ios::ate);
if(!in.good()) {
std::cout << "Invalid ini file: " << fileName << std::endl;
return false;
}
long size = in.tellg();
in.seekg(0, std::ios::beg);
std::string buf;
buf.resize(size);
in.read((char *)buf.data(), size);
bool ret = parse(buf);
return ret;
}
std::string dump() const
{
return
"[test]"
"target = gpu ; 使用cpu 或 gpu 运行"
"stream = rtmp://222.190.143.158:1735/live/xcp1 ; rtmp流地址"
"confThreshold = 0.2 ; 检测准确阈值0.2-0.99, 越大越准确"
;
}
void dumpFile(const std::string &fileName = "cfg.ini")
{
std::ofstream out(fileName, std::ios::out | std::ios::binary);
auto dmp = dump();
out.write(dmp.data(), dmp.size());
}
private:
std::vector<std::string> tokenize(const std::string &self, const std::string &chars) const
{
std::vector<std::string> tokens(1);
std::string map(256, '\0');
for(const unsigned char &ch : chars) {
map[ch] = '\1';
}
for(const unsigned char &ch : self) {
if(!map.at(ch)) {
tokens.back().push_back(char(ch));
}
else if(!tokens.back().empty()) {
tokens.emplace_back();
}
}
while(!tokens.empty() && tokens.back().empty()) {
tokens.pop_back();
}
return tokens;
}
};
4、测试
/*
config
--- cfg.ini ---
[test]
target = gpu ; 使用cpu 或 gpu 运行
stream = rtmp://222.190.143.158:1735/live/xcp1 ; rtmp流地址"
confThreshold = 0.2 ; 检测准确阈值0.2-0.99, 越大越准确
*/
ConfigParser cfg;
bool ret = cfg.parseFile("cfg.ini");
if(!ret) {
cfg.dumpFile("cfg.ini");
std::cout << "Cfg file is generated, modify it and retry." << std::endl;
return 0;
}
std::string cfg_target = cfg["test"]["target"];
std::string cfg_stram = cfg["test"]["stream"];
// std::string cfg_confThreshold = cfg["test"]["confThreshold"];
float cfg_confThreshold = cfg["test"]["confThreshold"];
本文介绍了一个使用C++实现的INI配置文件解析器,详细解释了其设计思路和实现过程,包括variant结构定义、字符串trim函数、ConfigureParser类的实现以及测试方法。
1万+

被折叠的 条评论
为什么被折叠?



