From f5d5087fb7920fff236a821c68c5945c68a1555a Mon Sep 17 00:00:00 2001 From: p9evjq4zb <2175226286@qq.com> Date: Mon, 20 Jan 2025 16:47:18 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- inifile.cpp | 811 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 811 insertions(+) create mode 100644 inifile.cpp diff --git a/inifile.cpp b/inifile.cpp new file mode 100644 index 0000000..36a7589 --- /dev/null +++ b/inifile.cpp @@ -0,0 +1,811 @@ + +#include +#include +#include +#include +#include +#include "inifile.h" + +namespace inifile +{ + +// 构造函数,会初始化注释字符集合flags_(容器),目前只使用#和;作为注释前缀 +IniFile::IniFile() +:commentDelimiter("#") +{ +} + +//解析一行数据,得到键值 +/* --------------------------------------------------------------------------*/ +/** + * @brief parse + * + * @param content 数据源指针 + * @param key 键名 + * @param value 键值 + * @param c 分隔符 + * + * @return bool + */ +/* ----------------------------------------------------------------------------*/ +bool IniFile::parse(const string &content, string *key, string *value) +{ + return split(content, "=", key, value); +} + +int IniFile::UpdateSection(const string &cleanLine, const string &comment, + const string &rightComment, IniSection **section) +{ + IniSection *newSection; + // 查找右中括号 + size_t index = cleanLine.find_first_of(']'); + if (index == string::npos) { + errMsg = string("no matched ] found"); + return ERR_UNMATCHED_BRACKETS; + } + + int len = index - 1; + // 若段名为空,继续下一行 + if (len <= 0) { + errMsg = string("section name is empty"); + return ERR_SECTION_EMPTY; + } + + // 取段名 + string s(cleanLine, 1, len); + + trim(s); + + //检查段是否已存在 + if (getSection(s) != NULL) { + errMsg = string("section ") + s + string("already exist"); + return ERR_SECTION_ALREADY_EXISTS; + } + + // 申请一个新段,由于map容器会自动排序,打乱原有顺序,因此改用vector存储(sections_vt) + newSection = new IniSection(); + // 填充段名 + newSection->name = s; + // 填充段开头的注释 + newSection->comment = comment; + newSection->rightComment = rightComment; + + sections_vt.push_back(newSection); + + *section = newSection; + + return 0; +} + +int IniFile::AddKeyValuePair(const string &cleanLine, const string &comment, + const string &rightComment, IniSection *section) +{ + string key, value; + + if (!parse(cleanLine, &key, &value)) { + errMsg = string("parse line failed:") + cleanLine; + return ERR_PARSE_KEY_VALUE_FAILED; + } + + IniItem item; + item.key = key; + item.value = value; + item.comment = comment; + item.rightComment = rightComment; + + section->items.push_back(item); + + return 0; +} + +int IniFile::Load(const string &filePath) +{ + int err; + string line; // 带注释的行 + string cleanLine; // 去掉注释后的行 + string comment; + string rightComment; + IniSection *currSection = NULL; // 初始化一个字段指针 + + release(); + + iniFilePath = filePath; + std::ifstream ifs(iniFilePath); + if (!ifs.is_open()) { + errMsg = string("open") +iniFilePath+ string(" file failed"); + return ERR_OPEN_FILE_FAILED; + } + + //增加默认段,即 无名段"" + currSection = new IniSection(); + currSection->name = ""; + sections_vt.push_back(currSection); + + // 每次读取一行内容到line + while (std::getline(ifs, line)) { + trim(line); + + // step 0,空行处理,如果长度为0,说明是空行,添加到comment,当作是注释的一部分 + if (line.length() <= 0) { + comment += delim; + continue; + } + + // step 1 + // 如果行首不是注释,查找行尾是否存在注释 + // 如果该行以注释开头,添加到comment,跳过当前循环,continue + if (IsCommentLine(line)) { + comment += line + delim; + continue; + } + + // 如果行首不是注释,查找行尾是否存在注释,若存在,切割该行,将注释内容添加到rightComment + split(line, commentDelimiter, &cleanLine, &rightComment); + + // step 2,判断line内容是否为段或键 + //段开头查找 [ + if (cleanLine[0] == '[') { + err = UpdateSection(cleanLine, comment, rightComment, &currSection); + } else { + // 如果该行是键值,添加到section段的items容器 + err = AddKeyValuePair(cleanLine, comment, rightComment, currSection); + } + + if (err != 0) { + ifs.close(); + return err; + } + + // comment清零 + comment = ""; + rightComment = ""; + } + + ifs.close(); + + return 0; +} + +int IniFile::Save() +{ + return SaveAs(iniFilePath); +} + +int IniFile::SaveAs(const string &filePath) +{ + string data = ""; + + /* 载入section数据 */ + for (IniSection_it sect = sections_vt.begin(); sect != sections_vt.end(); ++sect) { + if ((*sect)->comment != "") { + data += (*sect)->comment; + } + + if ((*sect)->name != "") { + data += string("[") + (*sect)->name + string("]"); + data += delim; + } + + if ((*sect)->rightComment != "") { + data += " " + commentDelimiter +(*sect)->rightComment; + } + + /* 载入item数据 */ + for (IniSection::IniItem_it item = (*sect)->items.begin(); item != (*sect)->items.end(); ++item) { + if (item->comment != "") { + data += item->comment; + if (data[data.length()-1] != '\n') { + data += delim; + } + } + + data += item->key + "=" + item->value; + + if (item->rightComment != "") { + data += " " + commentDelimiter + item->rightComment; + } + + if (data[data.length()-1] != '\n') { + data += delim; + } + } + } + + std::ofstream ofs(filePath); + ofs << data; + ofs.close(); + return 0; +} + +IniSection *IniFile::getSection(const string §ion /*=""*/) +{ + for (IniSection_it it = sections_vt.begin(); it != sections_vt.end(); ++it) { + if ((*it)->name == section) { + return *it; + } + } + + return NULL; +} + +int IniFile::GetSections(vector *sections) +{ + for (IniSection_it it = sections_vt.begin(); it != sections_vt.end(); ++it) { + sections->push_back((*it)->name); + } + + return sections->size(); +} + +int IniFile::GetSectionNum() +{ + return sections_vt.size(); +} + +int IniFile::GetStringValue(const string §ion, const string &key, string *value) +{ + return getValue(section, key, value); +} + +int IniFile::GetIntValue(const string §ion, const string &key, int *intValue) +{ + int err; + string strValue; + + err = getValue(section, key, &strValue); + + *intValue = atoi(strValue.c_str()); + + return err; +} + +int IniFile::GetDoubleValue(const string §ion, const string &key, double *value) +{ + int err; + string strValue; + + err = getValue(section, key, &strValue); + + *value = atof(strValue.c_str()); + + return err; +} + +int IniFile::GetBoolValue(const string §ion, const string &key, bool *value) +{ + int err; + string strValue; + + err = getValue(section, key, &strValue); + + if (StringCmpIgnoreCase(strValue, "true") || StringCmpIgnoreCase(strValue, "1")) { + *value = true; + } else if (StringCmpIgnoreCase(strValue, "false") || StringCmpIgnoreCase(strValue, "0")) { + *value = false; + } + + return err; +} + +/* 获取section段第一个键为key的string值,成功返回获取的值,否则返回默认值 */ +void IniFile::GetStringValueOrDefault(const string §ion, const string &key, + string *value, const string &defaultValue) +{ + if (GetStringValue(section, key, value) != 0) { + *value = defaultValue; + } + + return; +} + +/* 获取section段第一个键为key的int值,成功返回获取的值,否则返回默认值 */ +void IniFile::GetIntValueOrDefault(const string §ion, const string &key, int *value, int defaultValue) +{ + if (GetIntValue(section, key, value) != 0) { + *value = defaultValue; + } + + return; +} + +/* 获取section段第一个键为key的double值,成功返回获取的值,否则返回默认值 */ +void IniFile::GetDoubleValueOrDefault(const string §ion, const string &key, double *value, double defaultValue) +{ + if (GetDoubleValue(section, key, value) != 0) { + *value = defaultValue; + } + + return; +} + +/* 获取section段第一个键为key的bool值,成功返回获取的值,否则返回默认值 */ +void IniFile::GetBoolValueOrDefault(const string §ion, const string &key, bool *value, bool defaultValue) +{ + if (GetBoolValue(section, key, value) != 0) { + *value = defaultValue; + } + + return; +} + +/* 获取注释,如果key=""则获取段注释 */ +int IniFile::GetComment(const string §ion, const string &key, string *comment) +{ + IniSection *sect = getSection(section); + + if (sect == NULL) { + errMsg = string("not find the section ")+section; + return ERR_NOT_FOUND_SECTION; + } + + if (key == "") { + *comment = sect->comment; + return RET_OK; + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + *comment = it->comment; + return RET_OK; + } + } + + errMsg = string("not find the key ")+section; + return ERR_NOT_FOUND_KEY; +} + +/* 获取行尾注释,如果key=""则获取段的行尾注释 */ +int IniFile::GetRightComment(const string §ion, const string &key, string *rightComment) +{ + IniSection *sect = getSection(section); + + if (sect == NULL) { + errMsg = string("not find the section ")+section; + return ERR_NOT_FOUND_SECTION; + } + + if (key == "") { + *rightComment = sect->rightComment; + return RET_OK; + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + *rightComment = it->rightComment; + return RET_OK; + } + } + + errMsg = string("not find the key ")+key; + return ERR_NOT_FOUND_KEY; +} + +int IniFile::getValue(const string §ion, const string &key, string *value) +{ + string comment; + return getValue(section, key, value, &comment); +} + +int IniFile::getValue(const string §ion, const string &key, string *value, string *comment) +{ + IniSection *sect = getSection(section); + + if (sect == NULL) { + errMsg = string("not find the section ")+section; + return ERR_NOT_FOUND_SECTION; + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + *value = it->value; + *comment = it->comment; + return RET_OK; + } + } + + errMsg = string("not find the key ")+key; + return ERR_NOT_FOUND_KEY; +} + +int IniFile::GetValues(const string §ion, const string &key, vector *values) +{ + vector comments; + return GetValues(section, key, values, &comments); +} + +int IniFile::GetValues(const string §ion, const string &key, vector *values, vector *comments) +{ + string value, comment; + + values->clear(); + comments->clear(); + + IniSection *sect = getSection(section); + + if (sect == NULL) { + errMsg = string("not find the section ")+section; + return ERR_NOT_FOUND_SECTION; + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + value = it->value; + comment = it->comment; + + values->push_back(value); + comments->push_back(comment); + } + } + + if (values->size() == 0) { + errMsg = string("not find the key ")+key; + return ERR_NOT_FOUND_KEY; + } + + return RET_OK; +} + +bool IniFile::HasSection(const string §ion) +{ + return (getSection(section) != NULL); +} + +bool IniFile::HasKey(const string §ion, const string &key) +{ + IniSection *sect = getSection(section); + + if (sect != NULL) { + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + return true; + } + } + } + + return false; +} + +int IniFile::setValue(const string §ion, const string &key, const string &value, const string &comment /*=""*/) +{ + IniSection *sect = getSection(section); + + string comt = comment; + + if (comt != "") { + comt = commentDelimiter + comt; + } + + if (sect == NULL) { + //如果段不存在,新建一个 + sect = new IniSection(); + + if (sect == NULL) { + errMsg = string("no enough memory!"); + return ERR_NO_ENOUGH_MEMORY; + } + + sect->name = section; + if (sect->name == "") { + // 确保空section在第一个 + sections_vt.insert(sections_vt.begin(), sect); + } else { + sections_vt.push_back(sect); + } + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + it->value = value; + it->comment = comt; + return RET_OK; + } + } + + // not found key + IniItem item; + item.key = key; + item.value = value; + item.comment = comt; + + sect->items.push_back(item); + + return RET_OK; +} + +int IniFile::SetStringValue(const string §ion, const string &key, const string &value) +{ + return setValue(section, key, value); +} + +int IniFile::SetIntValue(const string §ion, const string &key, int value) +{ + char buf[64] = {0}; + snprintf(buf, sizeof(buf), "%d", value); + return setValue(section, key, buf); +} + +int IniFile::SetDoubleValue(const string §ion, const string &key, double value) +{ + char buf[64] = {0}; + snprintf(buf, sizeof(buf), "%f", value); + return setValue(section, key, buf); +} + +int IniFile::SetBoolValue(const string §ion, const string &key, bool value) +{ + if (value) { + return setValue(section, key, "true"); + } else { + return setValue(section, key, "false"); + } +} + +int IniFile::SetComment(const string §ion, const string &key, const string &comment) +{ + IniSection *sect = getSection(section); + + if (sect == NULL) { + errMsg = string("Not find the section ")+section; + return ERR_NOT_FOUND_SECTION; + } + + if (key == "") { + sect->comment = comment; + return RET_OK; + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + it->comment = comment; + return RET_OK; + } + } + + errMsg = string("not find the key ")+key; + return ERR_NOT_FOUND_KEY; +} + +int IniFile::SetRightComment(const string §ion, const string &key, const string &rightComment) +{ + IniSection *sect = getSection(section); + + if (sect == NULL) { + errMsg = string("Not find the section ")+section; + return ERR_NOT_FOUND_SECTION; + } + + if (key == "") { + sect->rightComment = rightComment; + return RET_OK; + } + + for (IniSection::IniItem_it it = sect->begin(); it != sect->end(); ++it) { + if (it->key == key) { + it->rightComment = rightComment; + return RET_OK; + } + } + + errMsg = string("not find the key ")+key; + return ERR_NOT_FOUND_KEY; +} + +void IniFile::SetCommentDelimiter(const string &delimiter) +{ + commentDelimiter = delimiter; +} + +void IniFile::DeleteSection(const string §ion) +{ + for (IniSection_it it = sections_vt.begin(); it != sections_vt.end(); ) { + if ((*it)->name == section) { + delete *it; + it = sections_vt.erase(it); + break; + } else { + it++; + } + } +} + +void IniFile::DeleteKey(const string §ion, const string &key) +{ + IniSection *sect = getSection(section); + + if (sect != NULL) { + for (IniSection::IniItem_it it = sect->begin(); it != sect->end();) { + if (it->key == key) { + it = sect->items.erase(it); + break; + } else { + it++; + } + } + } +} + +/*-------------------------------------------------------------------------*/ +/** + @brief release: 释放全部资源,清空容器 + @param none + @return none + */ +/*--------------------------------------------------------------------------*/ +void IniFile::release() +{ + iniFilePath = ""; + + for (IniSection_it it = sections_vt.begin(); it != sections_vt.end(); ++it) { + delete (*it); // 清除section + } + + sections_vt.clear(); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief 判断是否是注释 + @param str 一个string变量 + @return 如果是注释则为真 + */ +/*--------------------------------------------------------------------------*/ +bool IniFile::IsCommentLine(const string &str) +{ + return StartWith(str, commentDelimiter); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief print for debug + @param none + @return none + */ +/*--------------------------------------------------------------------------*/ +void IniFile::print() +{ + printf("############ print start ############\n"); + printf("filePath:[%s]\n", iniFilePath.c_str()); + printf("commentDelimiter:[%s]\n", commentDelimiter.c_str()); + + for (IniSection_it it = sections_vt.begin(); it != sections_vt.end(); ++it) { + printf("comment :[\n%s]\n", (*it)->comment.c_str()); + printf("section :\n[%s]\n", (*it)->name.c_str()); + if ((*it)->rightComment != "") { + printf("rightComment:\n%s", (*it)->rightComment.c_str()); + } + + for (IniSection::IniItem_it i = (*it)->items.begin(); i != (*it)->items.end(); ++i) { + printf(" comment :[\n%s]\n", i->comment.c_str()); + printf(" parm :%s=%s\n", i->key.c_str(), i->value.c_str()); + if (i->rightComment != "") { + printf(" rcomment:[\n%s]\n", i->rightComment.c_str()); + } + } + } + + printf("############ print end ############\n"); + return; +} + +const string & IniFile::GetErrMsg() +{ + return errMsg; +} + +bool IniFile::StartWith(const string &str, const string &prefix) +{ + if (strncmp(str.c_str(), prefix.c_str(), prefix.size()) == 0) { + return true; + } + + return false; +} + +void IniFile::trimleft(string &str, char c /*=' '*/) +{ + int len = str.length(); + + int i = 0; + + while (str[i] == c && str[i] != '\0') { + i++; + } + + if (i != 0) { + str = string(str, i, len - i); + } +} + +void IniFile::trimright(string &str, char c /*=' '*/) +{ + int i = 0; + int len = str.length(); + + for (i = len - 1; i >= 0; --i) { + if (str[i] != c) { + break; + } + } + + str = string(str, 0, i + 1); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief trim,整理一行字符串,去掉首尾空格 + @param str string变量 + */ +/*--------------------------------------------------------------------------*/ +void IniFile::trim(string &str) +{ + int len = str.length(); + + int i = 0; + + while ((i < len) && isspace(str[i]) && (str[i] != '\0')) { + i++; + } + + if (i != 0) { + str = string(str, i, len - i); + } + + len = str.length(); + + for (i = len - 1; i >= 0; --i) { + if (!isspace(str[i])) { + break; + } + } + + str = string(str, 0, i + 1); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief split,用分隔符切割字符串 + @param str 输入字符串 + @param left_str 分隔后得到的左字符串 + @param right_str 分隔后得到的右字符串(不包含seperator) + @param seperator 分隔符 + @return pos + */ +/*--------------------------------------------------------------------------*/ +bool IniFile::split(const string &str, const string &sep, string *pleft, string *pright) +{ + size_t pos = str.find(sep); + string left, right; + + if (pos != string::npos) { + left = string(str, 0, pos); + right = string(str, pos+1); + + trim(left); + trim(right); + + *pleft = left; + *pright = right; + return true; + } else { + left = str; + right = ""; + + trim(left); + + *pleft = left; + *pright = right; + return false; + } +} + +bool IniFile::StringCmpIgnoreCase(const string &str1, const string &str2) +{ + string a = str1; + string b = str2; + transform(a.begin(), a.end(), a.begin(), towupper); + transform(b.begin(), b.end(), b.begin(), towupper); + + return (a == b); +} + +} /* namespace inifile */