首页>Program>source

请查看我的简单.ini配置加载程序/保护程序:

//
// value wrapper
// 
class IniEntry {
public:
    //
    // default ctor
    IniEntry() {
    }
    //
    // copy ctor
    IniEntry(const IniEntry& rhs) : _Data(rhs._Data) {
    }
    //
    // move ctor
    IniEntry(IniEntry&& rhs) noexcept : _Data(std::move(rhs._Data)) {
    }
    //
    // dtor
    ~IniEntry() {
    }
public:
    //
    // copy assignment
    IniEntry& operator=(const IniEntry& rhs) {
        _Data = rhs._Data;
        return *this;
    }
    //
    // move assignment
    IniEntry& operator=(IniEntry&& rhs) noexcept {
        _Data = std::move(rhs._Data);
        return *this;
    }
    //
    // string assignment
    IniEntry& operator=(const char* value) {
        SetAsString(value);
        return *this;
    }
    //
    // integer assignment
    IniEntry& operator=(int value) {
        SetAsInteger(value);
        return *this;
    }
    //
    // float assignment
    IniEntry& operator=(float value) {
        SetAsFloat(value);
        return *this;
    }
public:
    //
    // string explicit cast
    explicit operator const char*() const {
        return GetAsString();
    }
    //
    // integer explicit cast
    explicit operator int() const {
        return GetAsInteger();
    }
    //
    // float explicit cast
    explicit operator float() const {
        return GetAsFloat();
    }
public:
    //
    // set data from string
    inline void SetAsString(const char* value) {
        _Data.assign(value);
    }
    //
    // get data as string
    inline const char* GetAsString(const char* default_return = "") const {
        return _Data.empty() ? default_return : _Data.c_str();
    }
    //
    // set data from integer
    inline void SetAsInteger(int value) {
        _Data.assign(std::to_string(value));
    }
    //
    // get data as integer
    inline int GetAsInteger(int default_return = 0) const {
        try {
            return std::stoi(_Data);
        }
        catch (...) {
            return default_return;
        }
    }
    //
    // set data from float
    inline void SetAsFloat(float value) {
        _Data.assign(std::to_string(value));
    }
    //
    // get data as float
    inline float GetAsFloat(float default_return = 0.f) const {
        try {
            return std::stof(_Data);
        }
        catch (...) {
            return default_return;
        }
    }
public:
    //
    // check if data empty
    bool Empty() const {
        return _Data.empty();
    }
private:
    std::string _Data;
};
//
// util object to restrict copy (and move if not declared move information)
//
class IniNonCopyable {
public:
    IniNonCopyable(const IniNonCopyable&) = delete;
    IniNonCopyable& operator=(const IniNonCopyable&) = delete;
};
//
// container controller base
//
template<class Element_t>
class IniContainer : public IniNonCopyable {
    typedef std::shared_ptr<Element_t>                              ContainerElement_t;
    typedef std::string                                             ContainerKey_t;
    typedef std::unordered_map<ContainerKey_t, ContainerElement_t>  Container_t;
public:
    //
    // default ctor
    IniContainer() {
    }
    //
    // dtor
    virtual ~IniContainer() {
    }
public:
    //
    // access to container element
    // if key does not exist - will be created new one
    Element_t& operator[](const std::string& key) {
        auto it = _Container.find(key);
        if (it != _Container.end()) {
            return *it->second.get();
        }
        auto instance = new Element_t;
        _Container.insert(std::make_pair(key, instance));
        return *instance;
    }
    //
    // check if container empty
    bool operator!() const {
        return Empty();
    }
public:
    //
    // create new element
    // if key already exist will be returned empty element
    virtual Element_t& Create(const std::string& key) {
        if (Has(key)) {
            static Element_t empty;
            return empty;
        }
        auto instance = new Element_t;
        _Container.insert(std::make_pair(key, instance));
        return *instance;
    }
    //
    // delete existed element
    virtual bool Delete(const std::string& key) {
        auto it = _Container.find(key);
        if (it == _Container.end()) {
            return false;
        }
        _Container.erase(key);
        return true;
    }
    //
    // access to container element
    // if key does not exist - will be returned empty element
    virtual Element_t& At(const std::string& key) const {
        auto it = _Container.find(key);
        if (it == _Container.end()) {
            static Element_t empty;
            return empty;
        }
        return *it->second.get();
    }
    //
    // check if element at key exist
    virtual bool Has(const std::string& key) const {
        return _Container.find(key) != _Container.end();
    }
    //
    // container clear
    virtual void Clear() {
        _Container.clear();
    }
    //
    // check if container empty
    virtual bool Empty() const {
        return _Container.empty();
    }
public:
    //
    // return ref to container data
    virtual const Container_t& GetContainer() const {
        return _Container;
    }
protected:
    Container_t         _Container;
};
//
// section handler (IniEntry container)
//
class IniSection : public IniContainer<IniEntry> {
public:
    //
    // default ctor
    IniSection() {
    }
    //
    // dtor
    virtual ~IniSection() {
    }
};
//
// file handler (IniSection container)
//
class IniFile : public IniContainer<IniSection> {
public:
    //
    // default ctor
    // if path not empty then loading will be started automatically
    explicit IniFile(const std::string& path = "") : _Path(path) {
        if (!_Path.empty()) {
            Load();
        }
    }
    //
    // dtor
    virtual ~IniFile() {
    }
public:
    //
    // file parsing
    // if path empty, then will be used ctor initialized path
    bool Load(const std::string& path = "") {
        const std::string file_path(path.empty() ? _Path : path);
        if (file_path.empty()) {
            return false;
        }
        std::ifstream file_stream(file_path);
        if (!file_stream) {
            return false;
        }
        const std::regex comment_regex(R"x(\s*[;#])x");
        const std::regex section_regex(R"x(\s*\[([^\]]+)\])x");
        const std::regex value_regex(R"x(\s*(\S[^ \t=]*)\s*=\s*((\s?\S+)+)\s*$)x");
        std::string file_line;
        std::smatch result;
        std::string section;
        while (std::getline(file_stream, file_line)) {
            if (file_line.empty() || std::regex_match(file_line, result, comment_regex)) {
            }
            else if (std::regex_match(file_line, result, section_regex) && result.size() == 2) {
                section.assign(result[1]);
                Create(section);
            }
            else if (std::regex_match(file_line, result, value_regex) && result.size() == 4) {
                At(section)[result[1]] = result[2].str().c_str();
            }
        }
        return true;
    }
    //
    // file saving
    // if path empty, then will be used ctor initialized path
    bool Save(const std::string& path = "") {
        const std::string file_path(path.empty() ? _Path : path);
        if (file_path.empty()) {
            return false;
        }
        std::ofstream file_stream(file_path);
        if (!file_stream) {
            return false;
        }
        for (auto& section : GetContainer()) {
            file_stream << "[" << section.first << "]" << std::endl;
            for (auto& entry : section.second->GetContainer()) {
                file_stream << entry.first << " = " << (const char*)*entry.second << std::endl;
            }
            file_stream << std::endl;
        }
        return true;
    }
private:
    std::string _Path;
};

该代码在同一文件中具有声明/实现,只是因为尚未完成.当然,将来会分开。

用法示例:

int main() {
    IniFile file("Test.ini");
    if (!file) {
        std::cout << "failed to load" << std::endl;
        return 0;
    }
    auto& general = file["General"];
    if (!general) {
        std::cout << "failed to pick section" << std::endl;
        return 0;
    }
    auto title  = general["Title"].GetAsString("Example");
    auto rate   = general["Rate"].GetAsInteger(100);
    auto scale  = general["Scale"].GetAsFloat(200.f);
    // update and save example
    general["Rate"] = (int)general["Rate"] + 1;
    if (!file.Save()) {
        std::cout << "failed to save" << std::endl;
        return 0;
    }
}
最新回答
  • 1月前
    1 #

    我们缺少一些必需的标头,包括:

    #include <fstream>
    #include <memory>
    #include <regex>
    #include <string>
    #include <unordered_map>
    #include <utility>
    

    在我们给 IniNonCopyable之前,代码不会编译   可访问的默认构造函数:

    protected:
        IniNonCopyable() = default;
    

    IniNonCopyable继承的类   应该私下继承它。


    以下划线开头,后跟一个大写字母的名称被保留用于任何目的,这意味着它们可能被预先定义为宏,从而导致非常模糊的错误; 避免使用这种命名约定。


    IniEntry似乎很奇怪   可以存放 float   但不是 double ; 同样, char*   但不是 std::string

    它的移动和复制构造函数,赋值和析构函数均未添加任何值,因此应省略。


    现代C ++风格更类似 using   到维兹维兹 :

    typedef
    

    这会将要定义的类型放在左侧,与其他C ++定义一致。

    小心定义以 using ContainerElement_t = std::shared_ptr<Element_t>; using ContainerKey_t = std::string; using Container_t = std::unordered_map<ContainerKey_t, ContainerElement_t>;结尾的类型 ; 仔细检查这些名称是否像在C语言中一样没有保留。


    我们不提供在已打开的流上工作的加载/保存方法,从而使代码难以测试-我们可以使用那些方法来编写和读取内存中的 _t   宾语.保持文件名界面为薄包装。


    在加载和保存时,我们在打开后立即检查流的状态,但是在关闭之后也应在最后检查.请注意,我们需要明确的 std::strstream  

  • 1月前
    2 #

    老实说,.ini是一种过时的格式,Microsoft认为已弃用。

    无论如何,您实际上不需要写任何东西-您可以使用 close()   -可以解析(读取和写入)ini,json和xml格式的属性树.而且使用起来非常简单.我不在代码中使用它的唯一原因是因为我是最近才知道的。

    无论如何,我碰巧也使用了ini格式,但是我发现使用section + key来访问数据的整个想法是很糟糕的选择.相反,我只是将所有数据存储在 boost::ptree中   格式为 map<string,string> .由于可以有效地创建小节(例如 cfg["section/key"] = "value";, )以及不带节的地址值.这种方式的ini格式变得与xml和json一样强大-与传统ini相比,使用这种格式具有主要优势.您为什么还认为.ini被Microsoft弃用?

    cfg["section/subsection/key"] = "value"; : 我不明白为什么你需要做 Alright, about your code   和 IniSection   具有虚拟析构函数和虚拟函数.您是否真的打算制作派生类来覆盖它们?

    此外,最好写 IniFile   和 IniFile() = default;   代替 virtual ~IniFile() = default;   和 IniFile(){}; ; 通常,它不重要(与当前情况一样),但对于琐碎的类,只要它们具有此类成员或从其派生,它就可能破坏分配/解除分配操作的性能。

    在C ++中通常很麻烦,但是要初始化从 virtual ~IniFile(){};加载文件   这不是一个好主意.开发代码很好用,但是通常您需要非英语文本的支持.在C ++ 20中,您可以简单地使用 std::string   而在当前版本中,将路径类视为输入-就像 std::u8string中的一种   或C ++ 17 std文件系统库中的一个。

    此外,您的输入/输出格式不应该是 boost::filesystem   但宁可   (甚至 int/float ),否则可能会丢失数据.这不是您需要性能的Class程-兼容性在这里更具value。

    由于这里的兼容性>性能,并且多线程环境中的多个类可能使用相同的ini文件类实例,因此最好通过 long long/double使其成为线程安全的

    long double

  • objective c:iPhone后台通过计时器进行网络连接
  • c++17:我的第二个子手游戏(C ++中的Hangman),如何避免嵌套的while循环? 如何坚持SRP?