#ifndef INI_READER_H_INCLUDED #define INI_READER_H_INCLUDED #include #include #include typedef std::map> ini_data_struct; typedef std::multimap string_multimap; class INIReader { /** * @brief A simple INI reader which utilize map and vector * to store sections and items, allowing access in logarithmic time. */ private: /** * @brief Internal parsed flag. */ bool parsed = false; std::string current_section; ini_data_struct ini_content; string_array exclude_sections, include_sections, read_sections; std::string cached_section; string_multimap cached_section_content; std::string isolated_items_section; bool chkIgnore(std::string section) { bool excluded = false, included = false; if(count(exclude_sections.begin(), exclude_sections.end(), section) > 0) excluded = true; if(include_sections.size() != 0) { if(count(include_sections.begin(), include_sections.end(), section) > 0) included = true; } else included = true; return excluded || !included; } public: /** * @brief Set this flag to true to do a UTF8-To-GBK conversion before parsing data. Only useful in Windows. */ bool do_utf8_to_gbk = false; /** * @brief Set this flag to true so any line within the section will be stored even it doesn't follow the "name=value" format. * These lines will store as the name "{NONAME}". */ bool store_any_line = false; /** * @brief Save isolated items before any section definitions. */ bool store_isolated_line = false; /** * @brief Allow a section title to appear multiple times. */ bool allow_dup_section_titles = false; /** * @brief Initialize the reader. */ INIReader() { parsed = false; } /** * @brief Parse a file during initialization. */ INIReader(std::string filePath) { parsed = false; ParseFile(filePath); } ~INIReader() { //nothing to do } /** * @brief Exclude a section with the given name. */ void ExcludeSection(std::string section) { exclude_sections.emplace_back(section); } /** * @brief Include a section with the given name. */ void IncludeSection(std::string section) { include_sections.emplace_back(section); } /** * @brief Set isolated items to given section. */ void SetIsolatedItemsSection(std::string section) { isolated_items_section = section; } /** * @brief Parse INI content into mapped data structure. * If exclude sections are set, these sections will not be stored. * If include sections are set, only these sections will be stored. */ int Parse(std::string content) //parse content into mapped data { if(!content.size()) //empty content return -1; //remove UTF-8 BOM removeUTF8BOM(content); bool inExcludedSection = false; std::string strLine, thisSection, curSection, itemName, itemVal; string_multimap itemGroup, existItemGroup; std::stringstream strStrm; unsigned int lineSize = 0; char delimiter = count(content.begin(), content.end(), '\n') < 1 ? '\r' : '\n'; EraseAll(); //first erase all data if(do_utf8_to_gbk && is_str_utf8(content)) content = UTF8ToGBK(content); //do conversion if flag is set if(store_isolated_line) curSection = isolated_items_section; //items before any section define will be store in this section strStrm< void GetIntArray(std::string section, std::string itemName, std::string separator, T &Array) { string_array vArray; unsigned int index, UBound = sizeof(Array) / sizeof(Array[0]); vArray = split(Get(section, itemName), separator); for(index = 0; index < vArray.size() && index < UBound; index++) Array[index] = stoi(vArray[index]); for(; index < UBound; index++) Array[index] = 0; } /** * @brief Retrieve a string style array with specific separator and write into integer array. */ template void GetIntArray(std::string itemName, std::string separator, T &Array) { if(current_section.size()) GetIntArray(current_section, itemName, separator, Array); } /** * @brief Add a std::string value with given values. */ int Set(std::string section, std::string itemName, std::string itemVal) { std::string value; if(!section.size()) return -1; if(!parsed) parsed = true; if(SectionExist(section)) { string_multimap &mapTemp = ini_content.at(section); mapTemp.insert(std::pair(itemName, itemVal)); } else { string_multimap mapTemp; mapTemp.insert(std::pair(itemName, itemVal)); ini_content.insert(std::pair>(section, mapTemp)); } return 0; } /** * @brief Add a string value with given values. */ int Set(std::string itemName, std::string itemVal) { if(!current_section.size()) return -1; return Set(current_section, itemName, itemVal); } /** * @brief Add a boolean value with given values. */ int SetBool(std::string section, std::string itemName, bool itemVal) { return Set(section, itemName, itemVal ? "true" : "false"); } /** * @brief Add a boolean value with given values. */ int SetBool(std::string itemName, bool itemVal) { return SetBool(current_section, itemName, itemVal); } /** * @brief Add a double value with given values. */ int SetDouble(std::string section, std::string itemName, double itemVal) { return Set(section, itemName, std::__cxx11::to_string(itemVal)); } /** * @brief Add a double value with given values. */ int SetDouble(std::string itemName, double itemVal) { return SetDouble(current_section, itemName, itemVal); } /** * @brief Add a long value with given values. */ int SetLong(std::string section, std::string itemName, long itemVal) { return Set(section, itemName, std::__cxx11::to_string(itemVal)); } /** * @brief Add a long value with given values. */ int SetLong(std::string itemName, long itemVal) { return SetLong(current_section, itemName, itemVal); } /** * @brief Add an array with the given separator. */ template int SetArray(std::string section, std::string itemName, std::string separator, T &Array) { std::string data; for(auto &x : Array) data += std::__cxx11::to_string(x) + separator; data = data.substr(0, data.size() - separator.size()); return Set(section, itemName, data); } /** * @brief Add an array with the given separator. */ template int SetArray(std::string itemName, std::string separator, T &Array) { return current_section.size() ? SetArray(current_section, itemName, separator, Array) : -1; } /** * @brief Erase all items with the given name. */ int Erase(std::string section, std::string itemName) { int retVal; if(!SectionExist(section)) return -1; retVal = ini_content.at(section).erase(itemName); if(retVal && cached_section == section) { cached_section_content = ini_content.at(section); } return retVal; } /** * @brief Erase all items with the given name. */ int Erase(std::string itemName) { return current_section.size() ? Erase(current_section, itemName) : -1; } /** * @brief Erase the first item with the given name. */ int EraseFirst(std::string section, std::string itemName) { string_multimap &mapTemp = ini_content.at(section); string_multimap::iterator iter = mapTemp.find(itemName); if(iter != mapTemp.end()) { mapTemp.erase(iter); if(cached_section == section) { cached_section_content = mapTemp; } return 0; } else { return -1; } } /** * @brief Erase the first item with the given name. */ int EraseFirst(std::string itemName) { return current_section.size() ? EraseFirst(current_section, itemName) : -1; } /** * @brief Erase all items in the given section. */ void EraseSection(std::string section) { if(ini_content.find(section) == ini_content.end()) return; eraseElements(ini_content.at(section)); if(cached_section == section) eraseElements(cached_section_content); } /** * @brief Erase all items in current section. */ void EraseSection() { if(current_section.size()) EraseSection(current_section); } /** * @brief Export the whole INI data structure into a string. */ std::string ToString() { std::string content; if(!parsed) return std::string(); for(auto &x : ini_content) { content += "[" + x.first + "]\n"; for(auto &y : x.second) { if(y.first != "{NONAME}") content += y.first + "="; content += y.second + "\n"; } content += "\n"; } return content.substr(0, content.size() - 2); } /** * @brief Export the whole INI data structure into a file. */ int ToFile(std::string filePath) { return fileWrite(filePath, ToString(), true); } }; #endif // INI_READER_H_INCLUDED