hikyuu2/hikyuu_cpp/hikyuu/utilities/IniParser.cpp
2019-11-10 19:45:57 +08:00

393 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* IniFile.cpp
*
* Created on: 2010-5-19
* Author: fasiondog
*/
#include <iostream>
#include <sstream>
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "IniParser.h"
namespace hku {
//用于记录分析文件时的格式错误信息(行号、错误的行内容)
//仅限IniParser内部使用
class ParsingError {
public:
ParsingError() {
m_haveError = false;
}
void append(size_t lineno, const std::string& line);
bool haveError() {
return m_haveError;
}
std::string str() {
return m_info.str();
}
private:
std::stringstream m_info;
bool m_haveError;
};
void ParsingError::append(size_t lineno, const std::string& line) {
m_info << "\n\t[line " << lineno << "] " << line;
if (!m_haveError) {
m_haveError = true;
}
}
IniParser::IniParser() {}
IniParser::~IniParser() {}
/**
* 读取并分析指定的ini文件
* @details 可多次读取不同的ini文件后再统一处理信息
* @throw std::invalid_argument 当指定的文件无法打开时,抛出该异常
* @throw std::logic_error 当文件格式错误时,抛出该异常
* @param filename 指定文件名
*/
void IniParser::read(const std::string& filename) {
std::ifstream inifile(filename.c_str(), std::ifstream::in);
if (!inifile) {
throw(std::invalid_argument("Can't read file(" + filename + ")!"));
}
size_t line_no = 0; //当前行号,用于记录错误信息
ParsingError parsing_error; //记录格式分析错误
std::string section;
std::string key;
std::string value;
std::string line_str;
while (std::getline(inifile, line_str)) {
line_no++;
boost::trim(line_str);
//空行或注释行,跳过
if (line_str.empty() || line_str.at(0) == ';') {
continue;
}
//检查第一个出现的注释符,并将其及其之后的字符清除
size_t pos = line_str.find(';');
if (pos != std::string::npos) {
line_str.assign(line_str, 0, pos);
boost::trim(line_str);
}
// section行
if (line_str.at(0) == '[') {
size_t len = line_str.size();
if (line_str[len - 1] != ']') {
parsing_error.append(line_no, line_str);
continue;
}
section.assign(line_str, 1, len - 2);
boost::trim(section);
if (section.empty()) {
parsing_error.append(line_no, line_str);
continue;
}
m_sections[section];
} else {
if (section.empty()) {
parsing_error.append(line_no, "Missing section header!");
break; //缺少section定义后续无须处理直接跳出循环
}
pos = line_str.find('=');
if (pos == std::string::npos || pos == line_str.size() - 1) {
parsing_error.append(line_no, line_str);
continue;
}
key.assign(line_str, 0, pos);
boost::trim(key);
if (key.empty()) {
parsing_error.append(line_no, line_str);
continue;
}
value.assign(line_str, pos + 1, std::string::npos);
boost::trim(value);
if (value.empty()) {
parsing_error.append(line_no, line_str);
continue;
}
m_sections[section][key] = value;
}
}
if (parsing_error.haveError()) {
inifile.close();
throw(std::logic_error(parsing_error.str()));
}
inifile.close();
}
/**
* 清除现有已读入的信息
*/
void IniParser::clear() {
m_sections.clear();
}
/**
* 判断指定的section是否存在
*/
bool IniParser::hasSection(const std::string& section) const {
return m_sections.count(section) ? true : false;
}
/**
* 判断指定option是否存在
*/
bool IniParser::hasOption(const std::string& section, const std::string& option) const {
if (m_sections.count(section) == 0) {
return false;
}
// if(m_sections[section].count(option) == 0) {
if (m_sections.find(section)->second.count(option) == 0) {
return false;
}
return true;
}
/**
* 获取所有的secton
* @return 所有的section列表
*/
IniParser::StringListPtr IniParser::getSectionList() const {
StringListPtr result(new std::list<std::string>);
section_map_type::const_iterator iter = m_sections.begin();
for (; iter != m_sections.end(); ++iter) {
result->push_back(iter->first);
}
return result;
}
/**
* 获取指定Section下的所有option
* @throw 若指定的section不存在将抛出std::invalid_argument异常
* @param section 指定的section
* @return 指定的section下所有option列表
*/
IniParser::StringListPtr IniParser::getOptionList(const std::string& section) const {
if (m_sections.count(section) == 0) {
throw(std::invalid_argument("No section: " + section));
}
StringListPtr result(new std::list<std::string>);
item_map_type option_map = m_sections.find(section)->second;
item_map_type::const_iterator iter = option_map.begin();
for (; iter != option_map.end(); ++iter) {
result->push_back(iter->first);
}
return result;
}
/**
* 获取指定的option值
* @throw std::invalid_argument 当指定option不存在对应值并且没有指定缺省值时抛出该异常
* @param section 指定的section
* @param option 指定的option
* @param default_str
* 缺省值在不存在对应的option值时返回该值。当该值为空时表示没有缺省值此时如果不存在对应option值
* 将抛出std::invalid_argument异常。默认为空没有指定缺省值。
*/
std::string IniParser::get(const std::string& section, const std::string& option,
const std::string& default_str) const {
std::string result;
if (m_sections.count(section) == 0) {
throw(std::invalid_argument("No section: " + section));
}
if (m_sections.find(section)->second.count(option) == 0) {
if (default_str.empty()) {
throw(std::invalid_argument("No option(" + option + ") in section(" + section + ")"));
} else {
result.assign(default_str);
boost::trim(result);
}
} else {
result.assign(m_sections.find(section)->second.find(option)->second);
}
return result;
}
/**
* 获取指定的option值并将其转换为int类型
* @throw std::invalid_argument 当指定option不存在对应值并且没有指定缺省值时抛出该异常;
* 或者当指定的缺省值非空无法转换为int类型时无论指定的option值是否存在都将抛出该异常。
* @throw std::domain_error 当指定的option值无法转换为int类型时抛出该异常
* @param section 指定的section
* @param option 指定的option
* @param default_str 缺省值。在不存在对应的option值时返回该值。当该值为空时表示没有缺省值。
* 默认为空,没有指定缺省值。
*/
int IniParser::getInt(const std::string& section, const std::string& option,
const std::string& default_str) const {
int result;
//先检查default_str是否可以转换为int
if (!default_str.empty()) {
try {
result = boost::lexical_cast<int>(default_str);
} catch (boost::bad_lexical_cast&) {
throw(std::invalid_argument("Invalid default value: " + default_str));
}
}
std::string value_str = get(section, option, default_str);
try {
result = boost::lexical_cast<int>(value_str);
} catch (boost::bad_lexical_cast& e) {
throw(std::domain_error(e.what()));
}
return result;
}
/**
* 获取指定的option值并将其转换为float类型
* @throw std::invalid_argument 当指定option不存在对应值并且没有指定缺省值时抛出该异常;
* 或者当指定的缺省值非空无法转换为float类型时无论指定的option值是否存在都将抛出该异常。
* @throw std::domain_error 当指定的option值无法转换为float类型时抛出该异常
* @param section 指定的section
* @param option 指定的option
* @param default_str 缺省值。在不存在对应的option值时返回该值。当该值为空时表示没有缺省值。
* 默认为空,没有指定缺省值。
*/
float IniParser::getFloat(const std::string& section, const std::string& option,
const std::string& default_str) const {
float result;
//先检查default_str是否可以转换为float
if (!default_str.empty()) {
try {
result = boost::lexical_cast<float>(default_str);
} catch (boost::bad_lexical_cast&) {
throw(std::invalid_argument("Invalid default value: " + default_str));
}
}
std::string value_str = get(section, option, default_str);
try {
result = boost::lexical_cast<float>(value_str);
} catch (boost::bad_lexical_cast& e) {
throw(std::domain_error(e.what()));
}
return result;
}
/**
* 获取指定的option值并将其转换为double类型
* @throw std::invalid_argument 当指定option不存在对应值并且没有指定缺省值时抛出该异常;
* 或者当指定的缺省值非空无法转换为double类型时无论指定的option值是否存在都将抛出该异常。
* @throw std::domain_error 当指定的option值无法转换为double类型时抛出该异常
* @param section 指定的section
* @param option 指定的option
* @param default_str 缺省值。在不存在对应的option值时返回该值。当该值为空时表示没有缺省值。
* 默认为空,没有指定缺省值。
*/
double IniParser::getDouble(const std::string& section, const std::string& option,
const std::string& default_str) const {
double result;
//先检查default_str是否可以转换为float
if (!default_str.empty()) {
try {
result = boost::lexical_cast<double>(default_str);
} catch (boost::bad_lexical_cast&) {
throw(std::invalid_argument("Invalid default value: " + default_str));
}
}
std::string value_str = get(section, option, default_str);
try {
result = boost::lexical_cast<double>(value_str);
} catch (boost::bad_lexical_cast& e) {
throw(std::domain_error(e.what()));
}
return result;
}
/**
* 获取指定的option值并将其转换为bool类型
* @details option表示为true时可接受的值为1|true|yes|on (不区分大小写) \n
* option表示为false时可接受的值为0|false|no|off (不区分大小写)
* @throw std::invalid_argument 当指定option不存在对应值并且没有指定缺省值时抛出该异常;
* 或者当指定的缺省值非空无法转换为bool类型时无论指定的option值是否存在都将抛出该异常。
* @throw std::domain_error 当指定的option值无法转换为bool类型时抛出该异常
* @param section 指定的section
* @param option 指定的option
* @param default_str 缺省值。在不存在对应的option值时返回该值。当该值为空时表示没有缺省值。
* 默认为空,没有指定缺省值。
*/
bool IniParser::getBool(const std::string& section, const std::string& option,
const std::string& default_str) const {
//先检查default_str是否可以转换为bool
std::string new_default_str(default_str);
if (!default_str.empty()) {
if (new_default_str != "1" && new_default_str != "0") {
boost::to_upper(new_default_str);
if (new_default_str == "TRUE" || new_default_str == "YES" || new_default_str == "ON") {
new_default_str.assign("1");
} else if (new_default_str == "FALSE" || new_default_str == "NO" ||
new_default_str == "OFF") {
new_default_str.assign("0");
} else {
throw(std::invalid_argument("Invalid default value: " + default_str));
}
}
}
std::string value_str = get(section, option, new_default_str);
if (value_str == "1") {
return true;
}
if (value_str == "0") {
return false;
}
boost::to_upper(value_str);
if (value_str == "TRUE" || value_str == "YES" || value_str == "ON") {
return true;
}
if (value_str == "FALSE" || value_str == "NO" || value_str == "OFF") {
return false;
}
throw(std::domain_error(value_str + " can not be translated to bool!"));
}
} // namespace hku