首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++中的ini文件解析器

C++中的ini文件解析器
EN

Code Review用户
提问于 2016-05-08 10:31:18
回答 2查看 12.6K关注 0票数 12

请查看我的ini文件解析器(可能还有一些通用的配置文件格式)。

所使用的数据结构是一节,见下文。这看起来是个好办法吗?

我在想那个名字。它将解析ini文件,但unix配置文件是可变的,因此调用类配置可能会过多地出售它。

我不支持中线评论。只有对一行的评论才会被完全忽略。

所有值都存储为字符串。我认为只有配置文件的用户才会知道在他或她的应用程序中是否应该使用一个值作为字符串或整数或其他任何东西,然后由用户来进行转换。

如有任何意见,将不胜感激。它的最初用途是用于Windows文件。将配置解析为各个部分。

config.hpp

代码语言:javascript
复制
#ifndef CONFIG_HPP_
#define CONFIG_HPP_

#include <string>
#include <unordered_map>
#include <list>


struct section
{
    std::string name;
    std::unordered_map<std::string, std::string> keyvalues;
};

class config
{
public:
    config(const std::string& filename);

    section* get_section(const std::string& sectionname);
    std::list<section>& get_sections();

    std::string get_value(const std::string& sectionname, const std::string& keyname);

private:
    void parse(const std::string& filename);

    std::list<section> sections;
};

#endif // CONFIG_HPP_

配置实现- config.cpp

代码语言:javascript
复制
#include "config.hpp"

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <unordered_map>
#include <list>


// trim leading white-spaces
static std::string& ltrim(std::string& s) {
    size_t startpos = s.find_first_not_of(" \t\r\n\v\f");
    if (std::string::npos != startpos)
    {
        s = s.substr(startpos);
    }
    return s;
}

// trim trailing white-spaces
static std::string& rtrim(std::string& s) {
    size_t endpos = s.find_last_not_of(" \t\r\n\v\f");
    if (std::string::npos != endpos)
    {
        s = s.substr(0, endpos + 1);
    }
    return s;
}

config::config(const std::string& filename) {
    parse(filename);
}

section* config::get_section(const std::string& sectionname) {
    std::list<section>::iterator found = std::find_if(sections.begin(), sections.end(), [sectionname](const section& sect) { 
        return sect.name.compare(sectionname) == 0; });

    return found != sections.end() ? &*found : NULL;
}

std::list<section>& config::get_sections() {
    return sections;
}

std::string config::get_value(const std::string& sectionname, const std::string&keyname) {
    section* sect = get_section(sectionname);
    if (sect != NULL) {
        std::unordered_map<std::string, std::string>::const_iterator it = sect->keyvalues.find(keyname);
        if (it != sect->keyvalues.end())
            return it->second;
    }
    return "";
}

void config::parse(const std::string& filename) {
    section currentsection;
    std::ifstream fstrm;
    fstrm.open(filename);

    if (!fstrm)
        throw std::invalid_argument(filename + " could not be opened");

    for (std::string line; std::getline(fstrm, line);)
    {
        // if a comment
        if (!line.empty() && (line[0] == ';' || line[0] == '#')) {
            // allow both ; and # comments at the start of a line

        }
        else if (line[0] == '[') {
            /* A "[section]" line */
            size_t end = line.find_first_of(']');
            if (end != std::string::npos) {

                // this is a new section so if we have a current section populated, add it to list
                if (!currentsection.name.empty()) {
                    sections.push_back(currentsection);  // copy
                    currentsection.name.clear();  // clear section for re-use
                    currentsection.keyvalues.clear();
                }
                currentsection.name = line.substr(1, end - 1);
            }
            else {
                // section has no closing ] char
            }
        }
        else if (!line.empty()) {
            /* Not a comment, must be a name[=:]value pair */
            size_t end = line.find_first_of("=:");
            if (end != std::string::npos) {
                std::string name = line.substr(0, end);
                std::string value = line.substr(end + 1);
                ltrim(rtrim(name));
                ltrim(rtrim(value));

                currentsection.keyvalues[name] = value;

        }
            else {
                // no key value delimitter
            }
        }
    } // for


    // if we are out of loop we add last section
    // this is a new section so if we have a current section populated, add it to list
    if (!currentsection.name.empty()) {
        sections.push_back(currentsection);  // copy
        currentsection.name = "";
        currentsection.keyvalues.clear();
    }
}

做一个简单测试的一些代码

代码语言:javascript
复制
#include "config.hpp"

#include <iostream>
#include <fstream>
#include <string>

void generate_config(const std::string& filename) {
    std::ofstream ostrm;
    ostrm.open(filename);
    if (ostrm) {
        ostrm << "[protocol]\nversion = 6     \n\n[user]\nname = Bob Smith       \nemail = bob@smith.com \nactive = true\n\npi = 3.14159";
    }
}


int main() {

    // generate test file
    generate_config("test1.ini");

    // retrieve some information from config file
    config cfg("test1.ini");
    section* usersection = cfg.get_section("user");

    if (usersection != NULL) {
        std::cout << "section name: " << usersection->name << std::endl;
        std::cout << "email=" << cfg.get_value("user", "email") << '\n';
    }
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2016-05-08 18:29:20

在我看来不错。以下是几点意见:

节* get_section(const::string& sectionname);std::list& get_sections();

不太确定返回数据的可变指针/引用是个好主意,因为更改内存中的section对底层文件表示没有任何影响。这可能会导致误解。最好通过返回const指针和const ref来保持数据的不可变性。这也将允许const对方法本身进行限定。

代码语言:javascript
复制
 const section* get_section(const std::string& sectionname) const;
 const std::list<section>& get_sections() const;

对于这些过于冗长的模板实例化,请使用auto

std::unordered_map::const_iterator it =sect->keyname.查找(Keyname);

与:

代码语言:javascript
复制
const auto it = sect->keyvalues.find(keyname);

这也将使生活更容易,如果容器或其内容在未来的变化。您只需更新keyvalues声明,而不是在任何地方都使用它。

另外,关于使用C++的当前特性集的问题,您应该更喜欢nullptr而不是NULL。因此,NULL隐式地转换为整数并导致函数重载解析混乱,因此要养成使用nullptr的习惯。

std::list还相关吗?有些人说,您应该忘记链接列表的存在,在这样一个CPU缓存和数据局部性对代码性能起着如此重要作用的世界里。对于大多数情况,std::vector是一个很好的默认设置,所以我将从向量开始。要帮助您做出决定,请看一下:效率与算法,性能与数据结构

票数 6
EN

Code Review用户

发布于 2016-05-31 15:51:51

具有单参数的

显式构造函数

为了避免编译器进行隐式单个arg转换,请考虑声明构造函数:

配置(const::string& filename);

作为:

显式配置(const::string& filename);

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/127819

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档