状元世家 發表於 2025-6-18 00:21:00

【C++】读取配置文件工具类

<h3 id="开发环境及功能">开发环境及功能</h3>
<p>开发环境:linux<br>
开发语言:C++<br>
编译工具:g++、cmake<br>
调试:gdb<br>
目的:使用C++实现一个读取配置文件的工具类,目的是读取key=value形式的配置,提高代码灵活性,解耦合。</p>
<h3 id="实现">实现</h3>
<ul>
<li>文件目录结构,未编译的目录</li>
</ul>
<pre><code>.
├── CMakeLists.txt
├── config.ini
├── include
│&nbsp;&nbsp; └── ConfigReader.h
├── main.cpp
└── src
    └── ConfigReader.cpp
</code></pre>
<h4 id="代码实现">代码实现</h4>
<ul>
<li>ConfigReader.h实现</li>
</ul>
<pre><code>#pragma once
#include &lt;string&gt;
#include &lt;stdexcept&gt;
#include &lt;map&gt;
#include &lt;unordered_map&gt;

class ConfigReader{
public:
      //explicit 禁用隐式调用,使用时,必须显示的调用
      explicit ConfigReader(const std::string&amp; fileName);
      // const std::string &amp;key 表示不会对key进行修改,不会修改传入的参数值 &amp;避免拷贝
      // const 表示是一个常量成员函数,不会修改类的任何成员变量
      std::string getString(const std::string &amp;key) const;
      int getInt(const std::string &amp;key) const;
      double getDouble(const std::string &amp;key) const;
      bool getBool(const std::string &amp;key) const;
private:
      //存放解析结果、
      //map 使用红黑树实现,键唯一,插入的键值对会按键的升序排序,查找、插入、删除速度为O(logn)
      //unordered_map使用hash表实现,键唯一,无序,平局查找、插入、删除时间复杂度为O(1),最坏o(n),占内存更多(维护hash表)
      //multimap使用红黑树实现,可以键重复,按键升序排列,查找、插入、删除速度为O(logn)
      std::unordered_map&lt;std::string,std::string&gt; configMap;

      //去除字符串空白字符
      static std::string trim(const std::string &amp; str);

      //解析配置文件
      void parseFile(const std::string &amp;fileName);


};

</code></pre>
<ul>
<li>ConfigReader.cpp</li>
</ul>
<pre><code>#include "ConfigReader.h"
#include &lt;fstream&gt;

ConfigReader::ConfigReader(const std::string &amp;fileName){

      parseFile(fileName);
}

//把所有非注释的行添加到map结构中
void ConfigReader::parseFile(const std::string &amp;fileName){

      std::ifstream file(fileName);
      if(!file.is_open()){
                throw std::runtime_error("Failed to open config file:"+fileName);
      }
      std::string line;
      while(std::getline(file,line)){
                //获取第一个#或/的位置
                size_t commentPos = line.find_first_of("#/");
                //如果该行有#或者/,则认为改行为注释行
                if(std::string::npos != commentPos){
                        continue;
                }
                size_t equalPos = line.find('=') != std::string::npos ? line.find('=') : line.find(':');
                if(std::string::npos == equalPos){
                        continue;
                }
                std::string key = trim(line.substr(0,equalPos));
                std::string value = trim(line.substr(equalPos+1));

                if(!key.empty()){
                        configMap = value;
                }

      }

}

//处理字符串首尾的空格
std::string ConfigReader::trim(const std::string&amp; str){
      if(str.empty()) return str;
      auto start = str.begin();
      while(start != str.end() &amp;&amp; std::isspace(*start)){
                start++;
      }
      auto end = str.end();
      do{
                end--;
      }while(std::distance(start,end)&gt;0 &amp;&amp; std::isspace(*end));
      return std::string(start,end+1);
}


std::string ConfigReader::getString(const std::string &amp; key) const{
      auto it = configMap.find(key);
      if(it == configMap.end()){
                throw std::out_of_range("config key not found:"+key);
      }
      return it-&gt;second;
}


intConfigReader::getInt(const std::string &amp; key) const{
      std::string value = getString(key);
      try{
                return std::stoi(value);
      }
      catch(const std::exception &amp; e){
                throw std::runtime_error("Invalid integer value for key"+key+":"+value);
      }
}

      }
      return it-&gt;second;
}

double ConfigReader::getDouble(const std::string &amp; key) const{
      std::string value = getString(key);
      try{
                return std::stod(value);
      }
      catch(const std::exception &amp; e){
                throw std::runtime_error("Invalid double value for key"+key+":"+value);
      }
}

bool ConfigReader::getBool(const std::string &amp; key) const{
      std::string value = getString(key);
      try{
                if(value == "true")
                        return true;
                else if(value == "false")
                        return false;
      }
      catch(const std::exception &amp; e){
                throw std::runtime_error("Invalid integer value for key"+key+":"+value);
      }
}
</code></pre>
<ul>
<li>main.cpp</li>
</ul>
<pre><code>#include &lt;iostream&gt;
#include "ConfigReader.h"

int main() {
    try {
      ConfigReader config("config.ini");

      std::string ip = config.getString("server_ip");
      int port = config.getInt("server_port");
      double timeout = config.getDouble("timeout");
      bool debug = config.getBool("debug_mode");
      std::string log_path = config.getString("log_path");

      std::cout &lt;&lt; "Server IP: " &lt;&lt; ip &lt;&lt; std::endl;
      std::cout &lt;&lt; "Server Port: " &lt;&lt; port &lt;&lt; std::endl;
      std::cout &lt;&lt; "Timeout: " &lt;&lt; timeout &lt;&lt; " seconds" &lt;&lt; std::endl;
      std::cout &lt;&lt; "Debug Mode: " &lt;&lt; (debug ? "ON" : "OFF") &lt;&lt; std::endl;
      std::cout &lt;&lt; "log_path: " &lt;&lt; log_path &lt;&lt; std::endl;
    } catch (const std::exception&amp; e) {
      std::cerr &lt;&lt; "Error: " &lt;&lt; e.what() &lt;&lt; std::endl;
      return 1;
    }

    return 0;
}
</code></pre>
<ul>
<li>CMakeLists.txt</li>
</ul>
<pre><code># 指定构建此项目所需的最低CMake版本为3.0
# 这确保CMake会检查版本兼容性,如果系统CMake版本低于3.0会报错
cmake_minimum_required(VERSION 3.0)

# 定义项目名称为ConfigReader
# 这会设置一些CMake内置变量如PROJECT_NAME,并初始化项目的基本配置
project(ConfigReader)

# 设置源代码目录路径变量
# ${CMAKE_SOURCE_DIR}是CMake内置变量,表示顶级CMakeLists.txt所在目录
# 这里将src子目录路径赋给SOURCE_DIR变量
set(SOURCE_DIR "${CMAKE_SOURCE_DIR}/src")

# 使用file(GLOB)命令收集所有.cpp源文件
# 这会匹配SOURCE_DIR目录下所有.cpp文件,并将文件列表存入SOURCE_FILES变量
# 注意:GLOB会在配置时立即执行,新增文件需要重新运行CMake
file(GLOB SOURCE_FILES "${SOURCE_DIR}/*.cpp")

# 设置包含目录路径变量
# 将include子目录路径赋给INCLUDE_DIR变量
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")

# 添加包含目录到编译器搜索路径
# 这样编译器就能找到INCLUDE_DIR目录下的头文件
include_directories(${INCLUDE_DIR})

# 设置编译器选项
# -g: 生成调试信息
# -std=c++11: 使用C++11标准
# -O2: 优化级别2
# -Wall: 启用所有警告
add_compile_options(-g -std=c++11 -O2 -Wall)

# 设置构建类型为Debug模式
# Debug模式会包含调试信息且不做优化,方便调试
# 可选值通常有: Debug, Release, RelWithDebInfo, MinSizeRel
set(CMAKE_BUILD_TYPE Debug)

# 定义要构建的可执行文件main
# 包含main.cpp和SOURCE_FILES中收集的所有源文件
add_executable(main main.cpp ${SOURCE_FILES})

</code></pre>
<ul>
<li>config.ini</li>
</ul>
<pre><code># 这是一个示例配置文件
server_ip = 192.168.1.100
server_port = 8080
timeout = 5.5
debug_mode = true
log_path = /var/log/myapp.log

</code></pre>
<h3 id="调试">调试</h3>
<ul>
<li>在项目终端进行编译
<blockquote>
<p>cmake .<br>
make<br>
./main</p>
</blockquote>
</li>
</ul>
<h4 id="使用gdb调试">使用gdb调试</h4>
<ul>
<li>在终端使用 gdb main</li>
</ul>
<pre><code>## 帮助
help / h
## 启动调试
gdb main
## 查看代码
list
## 运行
run / r运行到第一个断点
start运行到第一行执行程序
## 打断点
break / b 行号/函数名
## 查看所有断点
info b
info breakpoints
## 执行
next / n 下一步 不进函数
step / s 下一步 进函数
continute /c 跳转下一个断点
finish 结束当前函数
info 查看函数局部变量的值
## 退出
quit / q
## 输出
print / p 变量
p m_vector
p m_map
p *(m_vector._M_impl._start_)@m_vector.size()
display 追踪具体变量值
undisplay 取消追踪
watch 设置观察点 变量修改时打印显示
</code></pre><br><br>
来源:https://www.cnblogs.com/hjk-airl/p/18932139
頁: [1]
查看完整版本: 【C++】读取配置文件工具类