Loading

使用C++标准库解析ini格式文件,只提供解析后的表和写入函数,查询、修改等功能可自行添加函数

rw_ini_base

使用C++标准库解析ini格式文件,只提供解析后的表和写入函数,查询、修改等功能可自行添加函数
个人博客地址:https://www.blog.hiyj.cn/article/detail/114
源码链接:https://github.com/WindSnowLi/rw_ini_base

类型说明

节点基类

    /**
     * 节点基类
     */
    class node_base {
    public:
        // 行上注释
        std::vector<std::string> line_up_explain{};
        // 行尾注释
        std::string line_tail_explain{};
    };

section类

    /**
     * section节点类
     */
    class section : public node_base {
    public:
        // section名字
        std::string name{};
        // 所属键值表
        std::vector<key_value> table{};

        section() = default;

        explicit section(std::string name) : name(std::move(name)) {}

        bool operator==(const section &s) const {
            return name == s.name;
        }
    };

键值对类

    /**
     * key_value 键值对类
     */
    class key_value : public node_base {
    public:
        // 键
        std::string key{};
        // 值
        std::string value{};

        bool operator==(const key_value &k_v) const {
            return key == k_v.key;
        }
    };

封装问题

示例

class rw_ini_test : public rw_ini::rw_ini_base {
public:
    explicit rw_ini_test(const std::string &ini_path) : rw_ini::rw_ini_base(ini_path) {}
    // 从rw_ini::rw_ini_base类继承到了解析结果集std::vector<section> s_list
    // 以及写入函数rw_ini::rw_ini_base::write()
    // 可以封装自己喜欢的api
};

说明

可通过 std::vector<section> s_list 直接查找 section 对象并或其所属键值对列表,可直接进行修改值操作,修改完成后可执行写入函数写入文件。

完整实现

/*
MIT License

Copyright (c) 2021 WindSnowLi

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef RW_INI_RW_INI_BASE_H
#define RW_INI_RW_INI_BASE_H

#include <cstring>
#include <sstream>
#include <fstream>
#include <vector>

// 命名空间
namespace rw_ini
{

    class key_value;

    class section;

    /**
     * 节点基类
     */
    class node_base
    {
    public:
        // 行上注释
        std::vector<std::string> line_up_explain{};
        // 行尾注释
        std::string line_tail_explain{};
    };

    /**
     * section节点类
     */
    class section : public node_base
    {
    public:
        // section名字
        std::string name{};
        // 所属键值表
        std::vector<key_value> table{};

        section() = default;

        explicit section(std::string name) : name(std::move(name)) {}

        bool operator==(const section &s) const
        {
            return name == s.name;
        }
    };

    /**
     * key_value 键值对类
     */
    class key_value : public node_base
    {
    public:
        // 键
        std::string key{};
        // 值
        std::string value{};

        bool operator==(const key_value &k_v) const
        {
            return key == k_v.key;
        }
    };

    /**
     * 解析基类
     */
    class rw_ini_base
    {
    private:
        // ini文件路径
        std::string ini_path{};

        // 节点上部注释缓冲区
        std::vector<std::string> line_up_explain_buff{};

        /**
         * 解析新的行
         * @param line ini文件的一行
         */
        void parse_line(const std::string &line);

        /**
         * 解析行中的section
         * @param line ini文件的一行
         * @param start 由parse_line解析到的有效的字符位置
         */
        void parse_line_section(const std::string &line, int start = 0);

        /**
         * 解析行中的key-value对
         * @param line ini文件的一行
         * @param start 由parse_line解析到的有效的字符位置
         */
        void parse_line_map(const std::string &line, int start = 0);

        /**
         * 解析键值对中的key
         * @param line ini文件的一行
         * @param start 由parse_line_map解析到的有效的字符位置
         */
        void parse_line_key(const std::string &line, int start = 0);

        /**
         * 解析键值对中的value
         * @param line ini文件的一行
         * @param start 由parse_line_key解析到的有效的字符位置
         */
        void parse_line_value(const std::string &line, int start = 0);

        /**
         * 解析行上注释
         * @param line ini文件的一行
         * @param start 由parse_line解析到的有效的字符位置
         */
        void parse_line_up_explain(const std::string &line, int start = 0);

        /**
         * 解析行尾注释
         * @param line ini文件的一行
         * @param start 由parse_line_*解析到的有效的字符位置
         */
        void parse_line_tail_explain(const std::string &line, int start = 0);

    public:
        /**
         * 初始化ini文件路径,直接进行解析
         * @param ini_path ini文件路径
         */
        explicit rw_ini_base(const std::string &ini_path);

        // 整体内容表
        std::vector<section> s_list;

        // 文件尾部注释
        std::vector<std::string> tail_explain{};

        /**
         * 写入文件
         */
        void write();
    };

}

rw_ini::rw_ini_base::rw_ini_base(const std::string &ini_path)
{
    this->ini_path = ini_path;
    std::ifstream fp(this->ini_path, std::ios::in);
    std::string line;
    while (std::getline(fp, line))
    {
        this->parse_line(line);
    }
    fp.close();
    this->line_up_explain_buff.swap(this->tail_explain);
}

void rw_ini::rw_ini_base::parse_line(const std::string &line)
{
    // 空行直接略过
    if (line.empty() || line[0] == '\r' || line[0] == '\n')
    {
        return;
    }
    // 遍历一行,寻找第一个有效字符位置
    for (int i = 0; i < line.size(); i++)
    {
        // 略过开头空格
        if (line[i] == ' ')
        {
            continue;
            // 读取到[视为该行为section行
        }
        else if (line[i] == '[')
        {
            this->parse_line_section(line, i);
            // 读取到;或#视为该行为行上注释
        }
        else if (line[i] == ';' || line[i] == '#')
        {
            this->parse_line_up_explain(line, i);
        }
        else
        {
            //除此之外为键值对行
            this->parse_line_map(line, i);
        }
        break;
    }
}

void rw_ini::rw_ini_base::parse_line_section(const std::string &line, int start)
{
    // parse_line已读取到[,开始从下一个字符标记
    int i = start + 1;
    // 标记至]字符
    for (; i < line.length(); ++i)
    {
        if (line[i] == ']')
        {
            break;
        }
    }
    // 截取[]区间内的字符
    auto s = section(line.substr(start + 1, i - start - 1));
    // 如果行上注释不为空,则所属于当前section
    if (!this->line_up_explain_buff.empty())
    {
        s.line_up_explain.swap(this->line_up_explain_buff);
    }
    // 列表里放入sectio
    this->s_list.emplace_back(s);
    // 继续向后检索,查看是否有注释
    for (; i < line.length(); ++i)
    {
        if (line[i] == ';' || line[i] == '#')
        {
            // 解析注释
            this->parse_line_tail_explain(line, i);
            break;
        }
    }
}

void rw_ini::rw_ini_base::parse_line_map(const std::string &line, int start)
{
    // 已由parse_line进行了排除前导空格操作,识别到了key的第一个字符
    this->parse_line_key(line, start);
    // 如果行上注释不为空,则所属于当前key_value
    if (!this->line_up_explain_buff.empty())
    {
        this->s_list.rbegin()->table.rbegin()->line_up_explain.swap(this->line_up_explain_buff);
    }
}

void rw_ini::rw_ini_base::parse_line_key(const std::string &line, int start)
{
    // parse_line_map已识别至key的第一个有效字符,key_end_pos为key的结束位置
    int i = start, key_end_pos = start;
    // key缓冲字符
    std::string key_buff;
    for (; i < line.length(); ++i)
    {
        // key的读取截止到=之前
        if (line[i] != '=')
        {
            key_buff.push_back(line[i]);
        }
        else
        {
            break;
        }
        // =之前的空格不计
        if (line[i] != ' ')
        {
            key_end_pos = i;
        }
    }
    // 创建新的key_value对象
    this->s_list.rbegin()->table.emplace_back(key_value());
    // 截取除末尾空格,写入key
    this->s_list.rbegin()->table.rbegin()->key = key_buff.substr(0, key_end_pos - start + 1);
    //接下来识别value
    this->parse_line_value(line, i);
}

void rw_ini::rw_ini_base::parse_line_value(const std::string &line, int start)
{
    std::string value_buff;
    // 已由parse_line_key识别到了=
    int i = start;
    // =后为value开始位置,value_end_pos为value结束位置标记
    int value_start_pos = ++i, value_end_pos = 0;
    // 去除value前导空格
    for (; i < line.length(); ++i)
    {
        if (line[i] == ' ')
        {
            continue;
        }
        break;
    }
    for (; i < line.length(); ++i)
    {
        // 不是注释标识符则视为value字符
        if (line[i] != ';' && line[i] != '#')
        {
            value_buff.push_back(line[i]);
        }
        else
        {
            // 读到注释标识符移交给parse_line_tail_explain处理
            this->parse_line_tail_explain(line, i);
            break;
        }
        // 忽略末尾空格
        if (line[i] != ' ')
        {
            value_end_pos = i;
        }
    }
    // 截取除末尾空格的value
    auto value = value_buff.substr(0, value_end_pos - value_start_pos);
    // 如果自带引号,则去掉引号
    if (*value.begin() == '"' && *value.rbegin() == '"')
    {
        value = value.substr(1, value.length() - 2);
    }
    this->s_list.rbegin()->table.rbegin()->value = value;
}

void rw_ini::rw_ini_base::parse_line_up_explain(const std::string &line, int start)
{
    // 去除前导空格
    int i = start;
    for (; i < line.length(); ++i)
    {
        if (line[i] == ' ')
        {
            continue;
        }
        break;
    }
    // 行上注释直接追加至行上注释缓冲区
    this->line_up_explain_buff.emplace_back(line.substr(i + 1));
}

void rw_ini::rw_ini_base::parse_line_tail_explain(const std::string &line, int start)
{
    // 如果section键值对表为空,则行尾注释属于最后一个section
    if (this->s_list.rbegin()->table.empty())
    {
        this->s_list.rbegin()->line_tail_explain = line.substr(start + 1);
    }
    else
    {
        // 否则属于最后一个键值对
        this->s_list.rbegin()->table.rbegin()->line_tail_explain = line.substr(start + 1);
    }
}

void rw_ini::rw_ini_base::write()
{
    std::stringstream ss;
    for (auto &&i : this->s_list)
    {
        for (auto &&j : i.line_up_explain)
        {
            ss << "# " << j << '\n';
        }
        ss << '[' << i.name << ']';
        if (!i.line_tail_explain.empty())
        {
            ss << '\t' << i.line_tail_explain;
        }
        ss << '\n';
        for (auto &&j : i.table)
        {
            for (auto &&k : j.line_up_explain)
            {
                ss << "# " << k << '\n';
            }
            ss << j.key << " = " << j.value;
            if (!j.line_tail_explain.empty())
            {
                ss << '\t' << i.line_tail_explain;
            }
            ss << '\n';
        }
        ss << "\n";
    }
    std::ofstream fp(this->ini_path, std::ios::trunc);
    fp << ss.str();
    fp.close();
}

#endif //RW_INI_RW_INI_BASE_H

posted @ 2021-10-04 17:25  WindSnowLi  阅读(12)  评论(0编辑  收藏  举报