【C++ 参数解析】加载文件并读入参数作为变量使用

1. 前言

在C++ 大型程序中,经常涉及到许多参数变量,编译好程序后,需要调整参数测试,需要再编译很麻烦。或者直接从程序入口接入,但是参数太多也很麻烦。

因此想找一个能够加载文件,并解析参数变量的程序,类似ROS 的 parameter 参数服务器,可以直接加载参数变量。但其他编程中不一定使用ROS因此找到了一个加载参数的程序。【链接】

2. 程序解析

程序主要是利用了std::map容器,按行读取找到 “=” ,然后将【key】压入左边和【value】压入右边:

std::map<std::string, std::string>

然后再根据不同的需求,即不同的类型解析右边的字符串为需要的类型。

在去除空格制表位后,解析主要程序如下:
(一个关键字的值返回)

  template <typename T>
  T string_to_T(std::string const &val) const {
    std::istringstream istr(val);
    T returnVal;
    if (!(istr >> returnVal))
      std::cout << "CFG: Not a valid " + (std::string) typeid(T).name() +
                       " received!\n";
    return returnVal;
  }

3. 封装源程序和测试

- config_file.h


#ifndef CONFIG_FILE_H_
#define CONFIG_FILE_H_

#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <typeinfo>
#include <vector>

class ConfigFile {
 public:
  /**
   * \brief Constructor.
   * \param fName the location of the configuration file
   */
  ConfigFile(const std::string &fName);

  /**
   * \brief Extract all keys.
   * \return `false` if the configuration file cannot be found, `true` otherwise
   */
  bool ExtractKeys();

  /**
   * \brief Check if a key exists.
   * \param key the key
   * \return `false` if the configuration file cannot be found, `true` otherwise
   */
  bool keyExists(const std::string &key) const;

  /**
   * \brief Return the value at a given key.
   * \param key the key associated to the value
   * \param defaultValue default value of the given key
   * \return the value associated to the key
   */
  template <typename ValueType>
  ValueType getValueOfKey(const std::string &key,
                          ValueType const &defaultValue) const {
    if (!keyExists(key)) return defaultValue;

    return string_to_T<ValueType>(contents.find(key)->second);
  }

  /**
   * \brief Return the value at a given key as a `string`.
   * \param key the key
   * \param defaultValue default value of the given key
   * \return the value as a `string`
   */
  std::string getValueOfKeyAsString(const std::string &key,
                                    const std::string &defaultValue);

  /**
   * \brief Return the value at a given key as a `std::vector<double>`.
   * \param key the key
   * \param defaultValue default value of the given key
   * \return the value as a `std::vector<double>`
   */
  std::vector<double> getValueOfKeyAsStdVectorDouble(
      const std::string &key, const std::string &defaultValue);

  /**
   * \brief Return the value at a given key as a `std::vector<int>`.
   * \param key the key
   * \param defaultValue default value of the given key
   * \return the value as a `std::vector<int>`
   */
  std::vector<int> getValueOfKeyAsStdVectorInt(const std::string &key,
                                               const std::string &defaultValue);

  /**
   * \brief Convert value of type `T` to `string`.
   * \param value the value to be converted
   * \return the value as a `string`
   */
  template <typename T>
  std::string T_to_string(T const &val) const {
    std::ostringstream ostr;
    ostr << val;

    return ostr.str();
  }

  /**
   * \brief Convert value of type `string` to type `T`.
   * \param value the value to be converted
   * \return the value as type `T`
   */
  // 这一段很妙没用过
  template <typename T>
  T string_to_T(std::string const &val) const {
    std::istringstream istr(val);
    T returnVal;
    if (!(istr >> returnVal))
      std::cout << "CFG: Not a valid " + (std::string) typeid(T).name() +
                       " received!\n";

    return returnVal;
  }

 private:
  void removeComment(std::string &line) const;

  bool onlyWhitespace(const std::string &line) const;

  bool validLine(const std::string &line) const;

  void extractKey(std::string &key, size_t const &sepPos,
                  const std::string &line) const;

  void extractValue(std::string &value, size_t const &sepPos,
                    const std::string &line) const;

  void extractContents(const std::string &line);

  void parseLine(const std::string &line, size_t const lineNo);

  std::vector<double> stringToDouble(const std::string &str);

  std::vector<int> stringToInt(const std::string &str);

  std::map<std::string, std::string> contents;
  std::string fName;
};

#endif /* CONFIG_FILE_H_ */

- config_file.cpp

#include "config_file.h"


void ConfigFile::removeComment(std::string &line) const {
  if (line.find('#') != line.npos) {
    line.erase(line.find('#'));
  }
}

bool ConfigFile::onlyWhitespace(const std::string &line) const {
  return (line.find_first_not_of(' ') == line.npos);
}

bool ConfigFile::validLine(const std::string &line) const {
  std::string temp = line;
  temp.erase(0, temp.find_first_not_of("\t "));
  if (temp[0] == '=') {
    return false;
  }

  for (size_t i = temp.find('=') + 1; i < temp.length(); i++) {
    if (temp[i] != ' ') {
      return true;
    }
  }

  return false;
}

void ConfigFile::extractKey(std::string &key, size_t const &sepPos,
                            const std::string &line) const {
  key = line.substr(0, sepPos);
  if (key.find('\t') != line.npos || key.find(' ') != line.npos) {
    key.erase(key.find_first_of("\t "));
  }
}

void ConfigFile::extractValue(std::string &value, size_t const &sepPos,
                              const std::string &line) const {
  value = line.substr(sepPos + 1);
  value.erase(0, value.find_first_not_of("\t "));
  value.erase(value.find_last_not_of("\t ") + 1);
}

void ConfigFile::extractContents(const std::string &line) {
  std::string temp = line;
  temp.erase(0, temp.find_first_not_of("\t "));
  size_t sepPos = temp.find('=');

  std::string key, value;
  extractKey(key, sepPos, temp);
  extractValue(value, sepPos, temp);

  if (!keyExists(key)) {
    contents.insert(std::pair<std::string, std::string>(key, value));
  } else {
    std::cout << "CFG: Can only have unique key names!\n";
  }
}

void ConfigFile::parseLine(const std::string &line, size_t const lineNo) {
  if (line.find('=') == line.npos) {
    std::cout << "CFG: Couldn't find separator on line: " +
                     T_to_string(lineNo) + "\n";
  }

  if (!validLine(line)) {
    std::cout << "CFG: Bad format for line: " + T_to_string(lineNo) + "\n";
  }

  extractContents(line);
}

bool ConfigFile::ExtractKeys() {
  std::ifstream file;
  file.open(fName.c_str());
  if (!file) {
    std::cout << "Config file " + fName + " could not be found!\n";
    return false;
  }

  std::string line;
  size_t lineNo = 0;
  while (std::getline(file, line)) {
    lineNo++;
    std::string temp = line;

    if (temp.empty()) {
      continue;
    }

    removeComment(temp);
    if (onlyWhitespace(temp)) {
      continue;
    }

    parseLine(temp, lineNo);
  }

  file.close();
  return true;
}

ConfigFile::ConfigFile(const std::string &fName) { this->fName = fName; }

bool ConfigFile::keyExists(const std::string &key) const {
  return contents.find(key) != contents.end();
}

std::string ConfigFile::getValueOfKeyAsString(const std::string &key,
                                              const std::string &defaultValue) {
  if (!keyExists(key)) {
    return defaultValue;
  }

  return contents.find(key)->second;
}

std::vector<double> ConfigFile::getValueOfKeyAsStdVectorDouble(
    const std::string &key, const std::string &defaultValue) {
  std::string s = getValueOfKeyAsString(key, defaultValue);

  std::vector<double> vec = stringToDouble(s);

  return vec;
}

std::vector<int> ConfigFile::getValueOfKeyAsStdVectorInt(
    const std::string &key, const std::string &defaultValue) {
  std::string s = getValueOfKeyAsString(key, defaultValue);

  std::vector<int> vec = stringToInt(s);

  return vec;
}

std::vector<double> ConfigFile::stringToDouble(const std::string &str) {
  std::vector<double> values;
  std::stringstream ss(str);
  double v;

  while (ss >> v) {
    values.push_back(v);
    if (ss.peek() == ' ') {
      ss.ignore();
    }
  }

  return values;
}

std::vector<int> ConfigFile::stringToInt(const std::string &str) {
  std::vector<int> values;
  std::stringstream ss(str);
  double v;

  while (ss >> v) {
    values.push_back(v);
    if (ss.peek() == ' ') {
      ss.ignore();
    }
  }

  return values;
}

- config_file_test.cpp (测试程序)

#include "config_file.h"
#include <string>


int main(int argc, char * argv[])
{
    std::string file("1.cfg");
    ConfigFile config_file(file);
    config_file.ExtractKeys();
    
    // 读取一个值,并给出默认值
    // cfg中没有该关键字,查找不到,赋给默认值
    bool Vbool = config_file.getValueOfKey<bool>("test1", false);

    // 查找关键字返回int类型的vector
    std::vector<int> Vvint = config_file.getValueOfKeyAsStdVectorInt("test2", "2");

    // 查找关键字返回double类型
    double Vd = config_file.getValueOfKey<double>("test3", 20.0);

    // 查找关键字返回int类型
    int Vint =  config_file.getValueOfKey<int>("test4", 6);

    // 查找关键字返回string类型
    std::string Vstring = config_file.getValueOfKeyAsString("test5", "");

    // 查找关键字返回string类型的 vector
    std::vector<double> Vvstring = config_file.getValueOfKeyAsStdVectorDouble("test6", "-1 1 -1 1 -1 1");
}

- test.cfg (测试文件)

# ==== Robot Hand Geometry Parameters ====
# ========================================
#   finger_width: the width of the finger
#   outer_diameter: the diameter of the robot hand (= maximum aperture + 2 * finger width)
#   hand_depth: the finger length (measured from hand base to finger tip)
#   hand_height: the height of the hand
#   init_bite: the minimum amount of the object to be covered by the hand
# test1 = true
test2 = 2
test3 = 19.0
test5 = test
test6 = -1 1 -1 1 -1 23 9.1
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值