文章目录
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