ini配置文件读取ConfigureParse

本文介绍了一个使用C++实现的INI配置文件解析器,详细解释了其设计思路和实现过程,包括variant结构定义、字符串trim函数、ConfigureParser类的实现以及测试方法。

用于解析类似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"];
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aworkholic

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值