C++實(shí)現(xiàn)ini文件讀寫的示例代碼
介紹
一般的ini配置文件由節(jié)、鍵、值組成。
【參數(shù)】(鍵=值),例如 :key=value;
【節(jié)】:所有的參數(shù)都是以節(jié)(section)為單位結(jié)合在一起的。所有的section名稱都是獨(dú)占一行,并且section名字都被方括號包圍著([XXX])。在section聲明后的所有parameters都屬于該section。
例如:[section1]
所以一個包含節(jié),鍵,值的簡單ini配置文件,例如:
[port]
portName=port1
port=123
1.使用INIReader.h頭文件
1.INIReader.h
// Read an INI file into easy-to-access name/value pairs. // inih and INIReader are released under the New BSD license (see LICENSE.txt). // Go to the project home page for more info: // // https://github.com/benhoyt/inih /* inih -- simple .INI file parser inih is released under the New BSD license (see LICENSE.txt). Go to the project home page for more info: https://github.com/benhoyt/inih */ #ifndef __INI_H__ #define __INI_H__ /* Make this header file easier to include in C++ code */ #ifdef __cplusplus extern "C" { #endif #include <stdio.h> /* Typedef for prototype of handler function. */ typedef int (*ini_handler)(void* user, const char* section, const char* name, const char* value); /* Typedef for prototype of fgets-style reader function. */ typedef char* (*ini_reader)(char* str, int num, void* stream); /* Parse given INI-style file. May have [section]s, name=value pairs (whitespace stripped), and comments starting with ';' (semicolon). Section is "" if name=value pair parsed before any section heading. name:value pairs are also supported as a concession to Python's configparser. For each name=value pair parsed, call handler function with given user pointer as well as section, name, and value (data only valid for duration of handler call). Handler should return nonzero on success, zero on error. Returns 0 on success, line number of first error on parse error (doesn't stop on first error), -1 on file open error, or -2 on memory allocation error (only when INI_USE_STACK is zero). */ int ini_parse(const char* filename, ini_handler handler, void* user); /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't close the file when it's finished -- the caller must do that. */ int ini_parse_file(FILE* file, ini_handler handler, void* user); /* Same as ini_parse(), but takes an ini_reader function pointer instead of filename. Used for implementing custom or string-based I/O. */ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user); /* Nonzero to allow multi-line value parsing, in the style of Python's configparser. If allowed, ini_parse() will call the handler with the same name for each subsequent line parsed. */ #ifndef INI_ALLOW_MULTILINE #define INI_ALLOW_MULTILINE 1 #endif /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of the file. See http://code.google.com/p/inih/issues/detail?id=21 */ #ifndef INI_ALLOW_BOM #define INI_ALLOW_BOM 1 #endif /* Nonzero to allow inline comments (with valid inline comment characters specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match Python 3.2+ configparser behaviour. */ #ifndef INI_ALLOW_INLINE_COMMENTS #define INI_ALLOW_INLINE_COMMENTS 1 #endif #ifndef INI_INLINE_COMMENT_PREFIXES #define INI_INLINE_COMMENT_PREFIXES ";" #endif /* Nonzero to use stack, zero to use heap (malloc/free). */ #ifndef INI_USE_STACK #define INI_USE_STACK 1 #endif /* Stop parsing on first error (default is to keep parsing). */ #ifndef INI_STOP_ON_FIRST_ERROR #define INI_STOP_ON_FIRST_ERROR 0 #endif /* Maximum line length for any line in INI file. */ #ifndef INI_MAX_LINE #define INI_MAX_LINE 200 #endif #ifdef __cplusplus } #endif /* inih -- simple .INI file parser inih is released under the New BSD license (see LICENSE.txt). Go to the project home page for more info: https://github.com/benhoyt/inih */ #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS #endif #include <stdio.h> #include <ctype.h> #include <string.h> #if !INI_USE_STACK #include <stdlib.h> #endif #define MAX_SECTION 50 #define MAX_NAME 50 /* Strip whitespace chars off end of given string, in place. Return s. */ inline static char* rstrip(char* s) { char* p = s + strlen(s); while (p > s && isspace((unsigned char)(*--p))) *p = '\0'; return s; } /* Return pointer to first non-whitespace char in given string. */ inline static char* lskip(const char* s) { while (*s && isspace((unsigned char)(*s))) s++; return (char*)s; } /* Return pointer to first char (of chars) or inline comment in given string, or pointer to null at end of string if neither found. Inline comment must be prefixed by a whitespace character to register as a comment. */ inline static char* find_chars_or_comment(const char* s, const char* chars) { #if INI_ALLOW_INLINE_COMMENTS int was_space = 0; while (*s && (!chars || !strchr(chars, *s)) && !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { was_space = isspace((unsigned char)(*s)); s++; } #else while (*s && (!chars || !strchr(chars, *s))) { s++; } #endif return (char*)s; } /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ inline static char* strncpy0(char* dest, const char* src, size_t size) { strncpy(dest, src, size); dest[size - 1] = '\0'; return dest; } /* See documentation in header file. */ inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user) { /* Uses a fair bit of stack (use heap instead if you need to) */ #if INI_USE_STACK char line[INI_MAX_LINE]; #else char* line; #endif char section[MAX_SECTION] = ""; char prev_name[MAX_NAME] = ""; char* start; char* end; char* name; char* value; int lineno = 0; int error = 0; #if !INI_USE_STACK line = (char*)malloc(INI_MAX_LINE); if (!line) { return -2; } #endif /* Scan through stream line by line */ while (reader(line, INI_MAX_LINE, stream) != NULL) { lineno++; start = line; #if INI_ALLOW_BOM if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB && (unsigned char)start[2] == 0xBF) { start += 3; } #endif start = lskip(rstrip(start)); if (*start == ';' || *start == '#') { /* Per Python configparser, allow both ; and # comments at the start of a line */ } #if INI_ALLOW_MULTILINE else if (*prev_name && *start && start > line) { #if INI_ALLOW_INLINE_COMMENTS end = find_chars_or_comment(start, NULL); if (*end) *end = '\0'; rstrip(start); #endif /* Non-blank line with leading whitespace, treat as continuation of previous name's value (as per Python configparser). */ if (!handler(user, section, prev_name, start) && !error) error = lineno; } #endif else if (*start == '[') { /* A "[section]" line */ end = find_chars_or_comment(start + 1, "]"); if (*end == ']') { *end = '\0'; strncpy0(section, start + 1, sizeof(section)); *prev_name = '\0'; } else if (!error) { /* No ']' found on section line */ error = lineno; } } else if (*start) { /* Not a comment, must be a name[=:]value pair */ end = find_chars_or_comment(start, "=:"); if (*end == '=' || *end == ':') { *end = '\0'; name = rstrip(start); value = lskip(end + 1); #if INI_ALLOW_INLINE_COMMENTS end = find_chars_or_comment(value, NULL); if (*end) *end = '\0'; #endif rstrip(value); /* Valid name[=:]value pair found, call handler */ strncpy0(prev_name, name, sizeof(prev_name)); if (!handler(user, section, name, value) && !error) error = lineno; } else if (!error) { /* No '=' or ':' found on name[=:]value line */ error = lineno; } } #if INI_STOP_ON_FIRST_ERROR if (error) break; #endif } #if !INI_USE_STACK free(line); #endif return error; } /* See documentation in header file. */ inline int ini_parse_file(FILE* file, ini_handler handler, void* user) { return ini_parse_stream((ini_reader)fgets, file, handler, user); } /* See documentation in header file. */ inline int ini_parse(const char* filename, ini_handler handler, void* user) { FILE* file; int error; file = fopen(filename, "r"); if (!file) return -1; error = ini_parse_file(file, handler, user); fclose(file); return error; } #endif /* __INI_H__ */ #ifndef __INIREADER_H__ #define __INIREADER_H__ #include <map> #include <set> #include <string> // Read an INI file into easy-to-access name/value pairs. (Note that I've gone // for simplicity here rather than speed, but it should be pretty decent.) class INIReader { public: // Empty Constructor INIReader() {}; // Construct INIReader and parse given filename. See ini.h for more info // about the parsing. explicit INIReader(const std::string& filename); // Construct INIReader and parse given file. See ini.h for more info // about the parsing. explicit INIReader(FILE *file); // Return the result of ini_parse(), i.e., 0 on success, line number of // first error on parse error, or -1 on file open error. int ParseError() const; // Return the list of sections found in ini file const std::set<std::string>& Sections() const; // Get a string value from INI file, returning default_value if not found. std::string Get(const std::string& section, const std::string& name, const std::string& default_value) const; // Get an integer (long) value from INI file, returning default_value if // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). long GetInteger(const std::string& section, const std::string& name, long default_value) const; // Get a real (floating point double) value from INI file, returning // default_value if not found or not a valid floating point value // according to strtod(). double GetReal(const std::string& section, const std::string& name, double default_value) const; // Get a single precision floating point number value from INI file, returning // default_value if not found or not a valid floating point value // according to strtof(). float GetFloat(const std::string& section, const std::string& name, float default_value) const; // Get a boolean value from INI file, returning default_value if not found or if // not a valid true/false value. Valid true values are "true", "yes", "on", "1", // and valid false values are "false", "no", "off", "0" (not case sensitive). bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const; protected: int _error; std::map<std::string, std::string> _values; std::set<std::string> _sections; static std::string MakeKey(const std::string& section, const std::string& name); static int ValueHandler(void* user, const char* section, const char* name, const char* value); }; #endif // __INIREADER_H__ #ifndef __INIREADER__ #define __INIREADER__ #include <algorithm> #include <cctype> #include <cstdlib> inline INIReader::INIReader(const std::string& filename) { _error = ini_parse(filename.c_str(), ValueHandler, this); } inline INIReader::INIReader(FILE *file) { _error = ini_parse_file(file, ValueHandler, this); } inline int INIReader::ParseError() const { return _error; } inline const std::set<std::string>& INIReader::Sections() const { return _sections; } inline std::string INIReader::Get(const std::string& section, const std::string& name, const std::string& default_value) const { std::string key = MakeKey(section, name); return _values.count(key) ? _values.at(key) : default_value; } inline long INIReader::GetInteger(const std::string& section, const std::string& name, long default_value) const { std::string valstr = Get(section, name, ""); const char* value = valstr.c_str(); char* end; // This parses "1234" (decimal) and also "0x4D2" (hex) long n = strtol(value, &end, 0); return end > value ? n : default_value; } inline double INIReader::GetReal(const std::string& section, const std::string& name, double default_value) const { std::string valstr = Get(section, name, ""); const char* value = valstr.c_str(); char* end; double n = strtod(value, &end); return end > value ? n : default_value; } inline float INIReader::GetFloat(const std::string& section, const std::string& name, float default_value) const { std::string valstr = Get(section, name, ""); const char* value = valstr.c_str(); char* end; float n = strtof(value, &end); return end > value ? n : default_value; } inline bool INIReader::GetBoolean(const std::string& section, const std::string& name, bool default_value) const { std::string valstr = Get(section, name, ""); // Convert to lower case to make string comparisons case-insensitive std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") return true; else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") return false; else return default_value; } inline std::string INIReader::MakeKey(const std::string& section, const std::string& name) { std::string key = section + "=" + name; // Convert to lower case to make section/name lookups case-insensitive std::transform(key.begin(), key.end(), key.begin(), ::tolower); return key; } inline int INIReader::ValueHandler(void* user, const char* section, const char* name, const char* value) { INIReader* reader = (INIReader*)user; std::string key = MakeKey(section, name); if (reader->_values[key].size() > 0) reader->_values[key] += "\n"; reader->_values[key] += value; reader->_sections.insert(section); return 1; } #endif // __INIREADER__
2.test.ini
3.INIReaderTest.cpp
// Example that shows simple usage of the INIReader class #include <iostream> #include <sstream> #include "INIReader.h" std::string sections(INIReader &reader) { std::stringstream ss; std::set<std::string> sections = reader.Sections(); for (std::set<std::string>::iterator it = sections.begin(); it != sections.end(); ++it) ss << *it << ","; return ss.str(); } int main() { INIReader reader("test.ini"); if (reader.ParseError() < 0) { std::cout << "Can't load 'test.ini'\n"; return 1; } std::cout << "Config loaded from 'test.ini': found sections=" << sections(reader) << " version=" << reader.GetInteger("protocol", "version", -1) << ", name=" << reader.Get("user", "name", "UNKNOWN") << ", email=" << reader.Get("user", "email", "UNKNOWN") << ", multi=" << reader.Get("user", "multi", "UNKNOWN") << ", pi=" << reader.GetReal("user", "pi", -1) << ", active=" << reader.GetBoolean("user", "active", true) << "\n"; return 0; }
2.使用ini.h頭文件
現(xiàn)代 c++ 的另一個 .ini
解析器(為 cpp17
制作),靈感來自 inih 并進(jìn)行了擴(kuò)展。
1.ini.h
/** * Yet another .ini parser for modern c++ (made for cpp17), inspired and extend * from @benhoyt's inih. See project page: https://github.com/SSARCandy/ini-cpp */ #ifndef __INI_H__ #define __INI_H__ #include <ctype.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <algorithm> #include <cctype> #include <cstdlib> #include <fstream> #include <iostream> #include <iterator> #include <map> #include <set> #include <sstream> #include <string> #include <type_traits> #include <unordered_map> #include <vector> namespace inih { /* Typedef for prototype of handler function. */ typedef int (*ini_handler)(void* user, const char* section, const char* name, const char* value); /* Typedef for prototype of fgets-style reader function. */ typedef char* (*ini_reader)(char* str, int num, void* stream); #define INI_STOP_ON_FIRST_ERROR 1 #define INI_MAX_LINE 2000 #define INI_INITIAL_ALLOC 200 #define MAX_SECTION 50 #define MAX_NAME 50 #define INI_START_COMMENT_PREFIXES ";#" #define INI_INLINE_COMMENT_PREFIXES ";" /* Strip whitespace chars off end of given string, in place. Return s. */ inline static char* rstrip(char* s) { char* p = s + strlen(s); while (p > s && isspace((unsigned char)(*--p))) *p = '\0'; return s; } /* Return pointer to first non-whitespace char in given string. */ inline static char* lskip(const char* s) { while (*s && isspace((unsigned char)(*s))) s++; return (char*)s; } /* Return pointer to first char (of chars) or inline comment in given string, or pointer to null at end of string if neither found. Inline comment must be prefixed by a whitespace character to register as a comment. */ inline static char* find_chars_or_comment(const char* s, const char* chars) { int was_space = 0; while (*s && (!chars || !strchr(chars, *s)) && !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { was_space = isspace((unsigned char)(*s)); s++; } return (char*)s; } /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ inline static char* strncpy0(char* dest, const char* src, size_t size) { strncpy(dest, src, size - 1); dest[size - 1] = '\0'; return dest; } /* See documentation in header file. */ inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, void* user) { /* Uses a fair bit of stack (use heap instead if you need to) */ char* line; size_t max_line = INI_INITIAL_ALLOC; char* new_line; size_t offset; char section[MAX_SECTION] = ""; char prev_name[MAX_NAME] = ""; char* start; char* end; char* name; char* value; int lineno = 0; int error = 0; line = (char*)malloc(INI_INITIAL_ALLOC); if (!line) { return -2; } #if INI_HANDLER_LINENO #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) #else #define HANDLER(u, s, n, v) handler(u, s, n, v) #endif /* Scan through stream line by line */ while (reader(line, (int)max_line, stream) != NULL) { offset = strlen(line); while (offset == max_line - 1 && line[offset - 1] != '\n') { max_line *= 2; if (max_line > INI_MAX_LINE) max_line = INI_MAX_LINE; new_line = (char*)realloc(line, max_line); if (!new_line) { free(line); return -2; } line = new_line; if (reader(line + offset, (int)(max_line - offset), stream) == NULL) break; if (max_line >= INI_MAX_LINE) break; offset += strlen(line + offset); } lineno++; start = line; if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB && (unsigned char)start[2] == 0xBF) { start += 3; } start = lskip(rstrip(start)); if (strchr(INI_START_COMMENT_PREFIXES, *start)) { /* Start-of-line comment */ } else if (*start == '[') { /* A "[section]" line */ end = find_chars_or_comment(start + 1, "]"); if (*end == ']') { *end = '\0'; strncpy0(section, start + 1, sizeof(section)); *prev_name = '\0'; } else if (!error) { /* No ']' found on section line */ error = lineno; } } else if (*start) { /* Not a comment, must be a name[=:]value pair */ end = find_chars_or_comment(start, "=:"); if (*end == '=' || *end == ':') { *end = '\0'; name = rstrip(start); value = end + 1; end = find_chars_or_comment(value, NULL); if (*end) *end = '\0'; value = lskip(value); rstrip(value); /* Valid name[=:]value pair found, call handler */ strncpy0(prev_name, name, sizeof(prev_name)); if (!HANDLER(user, section, name, value) && !error) error = lineno; } else if (!error) { /* No '=' or ':' found on name[=:]value line */ error = lineno; } } if (error) break; } free(line); return error; } inline int ini_parse_file(FILE* file, ini_handler handler, void* user) { return ini_parse_stream((ini_reader)fgets, file, handler, user); } inline int ini_parse(const char* filename, ini_handler handler, void* user) { FILE* file; int error; file = fopen(filename, "r"); if (!file) return -1; error = ini_parse_file(file, handler, user); fclose(file); return error; } #endif /* __INI_H__ */ #ifndef __INIREADER_H__ #define __INIREADER_H__ // Read an INI file into easy-to-access name/value pairs. (Note that I've gone // for simplicity here rather than speed, but it should be pretty decent.) class INIReader { public: // Empty Constructor INIReader(){}; // Construct INIReader and parse given filename. See ini.h for more info // about the parsing. INIReader(std::string filename); // Construct INIReader and parse given file. See ini.h for more info // about the parsing. INIReader(FILE* file); // Return the result of ini_parse(), i.e., 0 on success, line number of // first error on parse error, or -1 on file open error. int ParseError() const; // Return the list of sections found in ini file const std::set<std::string> Sections() const; // Return the list of keys in the given section const std::set<std::string> Keys(std::string section) const; const std::unordered_map<std::string, std::string> Get( std::string section) const; template <typename T = std::string> T Get(const std::string& section, const std::string& name) const; template <typename T> T Get(const std::string& section, const std::string& name, T&& default_v) const; template <typename T = std::string> std::vector<T> GetVector(const std::string& section, const std::string& name) const; template <typename T> std::vector<T> GetVector(const std::string& section, const std::string& name, const std::vector<T>& default_v) const; template <typename T = std::string> void InsertEntry(const std::string& section, const std::string& name, const T& v); template <typename T = std::string> void InsertEntry(const std::string& section, const std::string& name, const std::vector<T>& vs); template <typename T = std::string> void UpdateEntry(const std::string& section, const std::string& name, const T& v); template <typename T = std::string> void UpdateEntry(const std::string& section, const std::string& name, const std::vector<T>& vs); protected: int _error; std::unordered_map<std::string, std::unordered_map<std::string, std::string>> _values; static int ValueHandler(void* user, const char* section, const char* name, const char* value); template <typename T> T Converter(const std::string& s) const; const bool BoolConverter(std::string s) const; template <typename T> std::string V2String(const T& v) const; template <typename T> std::string Vec2String(const std::vector<T>& v) const; }; #endif // __INIREADER_H__ #ifndef __INIREADER__ #define __INIREADER__ inline INIReader::INIReader(std::string filename) { _error = ini_parse(filename.c_str(), ValueHandler, this); ParseError(); } inline INIReader::INIReader(FILE* file) { _error = ini_parse_file(file, ValueHandler, this); ParseError(); } inline int INIReader::ParseError() const { switch (_error) { case 0: break; case -1: throw std::runtime_error("ini file not found."); case -2: throw std::runtime_error("memory alloc error"); default: throw std::runtime_error("parse error on line no: " + std::to_string(_error)); } return 0; } /** * @brief Return the list of sections found in ini file * @return The list of sections found in ini file * * */ inline const std::set<std::string> INIReader::Sections() const { std::set<std::string> retval; for (auto const& element : _values) { retval.insert(element.first); } return retval; } /** * @brief Return the list of keys in the given section * @param section The section name * @return The list of keys in the given section */ inline const std::set<std::string> INIReader::Keys(std::string section) const { auto const _section = Get(section); std::set<std::string> retval; for (auto const& element : _section) { retval.insert(element.first); } return retval; } inline const std::unordered_map<std::string, std::string> INIReader::Get( std::string section) const { auto const _section = _values.find(section); if (_section == _values.end()) { throw std::runtime_error("section '" + section + "' not found."); } return _section->second; } /** * @brief Return the value of the given key in the given section * @param section The section name * @param name The key name * @return The value of the given key in the given section */ template <typename T> inline T INIReader::Get(const std::string& section, const std::string& name) const { auto const _section = Get(section); auto const _value = _section.find(name); if (_value == _section.end()) { throw std::runtime_error("key '" + name + "' not found in section '" + section + "'."); } std::string value = _value->second; if constexpr (std::is_same<T, std::string>()) { return value; } else if constexpr (std::is_same<T, bool>()) { return BoolConverter(value); } else { return Converter<T>(value); }; } /** * @brief Return the value of the given key in the given section, return default * if not found * @param section The section name * @param name The key name * @param default_v The default value * @return The value of the given key in the given section, return default if * not found */ template <typename T> inline T INIReader::Get(const std::string& section, const std::string& name, T&& default_v) const { try { return Get<T>(section, name); } catch (std::runtime_error& e) { return default_v; } } /** * @brief Return the value array of the given key in the given section. * @param section The section name * @param name The key name * @return The value array of the given key in the given section. * * For example: * ```ini * [section] * key = 1 2 3 4 * ``` * ```cpp * const auto vs = ini.GetVector<std::vector<int>>("section", "key"); * // vs = {1, 2, 3, 4} * ``` */ template <typename T> inline std::vector<T> INIReader::GetVector(const std::string& section, const std::string& name) const { std::string value = Get(section, name); std::istringstream out{value}; const std::vector<std::string> strs{std::istream_iterator<std::string>{out}, std::istream_iterator<std::string>()}; try { std::vector<T> vs{}; for (const std::string& s : strs) { vs.emplace_back(Converter<T>(s)); } return vs; } catch (std::exception& e) { throw std::runtime_error("cannot parse value " + value + " to vector<T>."); } } /** * @brief Return the value array of the given key in the given section, return * default if not found * @param section The section name * @param name The key name * @param default_v The default value * @return The value array of the given key in the given section, return default * if not found * * @see INIReader::GetVector */ template <typename T> inline std::vector<T> INIReader::GetVector( const std::string& section, const std::string& name, const std::vector<T>& default_v) const { try { return GetVector<T>(section, name); } catch (std::runtime_error& e) { return default_v; }; } template <typename T> inline void INIReader::InsertEntry(const std::string& section, const std::string& name, const T& v) { if (_values[section][name].size() > 0) { throw std::runtime_error("duplicate key '" + std::string(name) + "' in section '" + section + "'."); } _values[section][name] = V2String(v); } template <typename T> inline void INIReader::InsertEntry(const std::string& section, const std::string& name, const std::vector<T>& vs) { if (_values[section][name].size() > 0) { throw std::runtime_error("duplicate key '" + std::string(name) + "' in section '" + section + "'."); } _values[section][name] = Vec2String(vs); } template <typename T> inline void INIReader::UpdateEntry(const std::string& section, const std::string& name, const T& v) { if (!_values[section][name].size()) { throw std::runtime_error("key '" + std::string(name) + "' not exist in section '" + section + "'."); } _values[section][name] = V2String(v); } template <typename T> inline void INIReader::UpdateEntry(const std::string& section, const std::string& name, const std::vector<T>& vs) { if (!_values[section][name].size()) { throw std::runtime_error("key '" + std::string(name) + "' not exist in section '" + section + "'."); } _values[section][name] = Vec2String(vs); } template <typename T> inline std::string INIReader::V2String(const T& v) const { std::stringstream ss; ss << v; return ss.str(); } template <typename T> inline std::string INIReader::Vec2String(const std::vector<T>& v) const { if (v.empty()) { return ""; } std::ostringstream oss; std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(oss, " ")); oss << v.back(); return oss.str(); } template <typename T> inline T INIReader::Converter(const std::string& s) const { try { T v{}; std::istringstream _{s}; _.exceptions(std::ios::failbit); _ >> v; return v; } catch (std::exception& e) { throw std::runtime_error("cannot parse value '" + s + "' to type<T>."); }; } inline const bool INIReader::BoolConverter(std::string s) const { std::transform(s.begin(), s.end(), s.begin(), ::tolower); static const std::unordered_map<std::string, bool> s2b{ {"1", true}, {"true", true}, {"yes", true}, {"on", true}, {"0", false}, {"false", false}, {"no", false}, {"off", false}, }; auto const value = s2b.find(s); if (value == s2b.end()) { throw std::runtime_error("'" + s + "' is not a valid boolean value."); } return value->second; } inline int INIReader::ValueHandler(void* user, const char* section, const char* name, const char* value) { INIReader* reader = (INIReader*)user; if (reader->_values[section][name].size() > 0) { throw std::runtime_error("duplicate key '" + std::string(name) + "' in section '" + section + "'."); } reader->_values[section][name] = value; return 1; } #endif // __INIREADER__ #ifndef __INIWRITER_H__ #define __INIWRITER_H__ class INIWriter { public: INIWriter(){}; inline static void write(const std::string& filepath, const INIReader& reader) { if (struct stat buf; stat(filepath.c_str(), &buf) == 0) { throw std::runtime_error("file: " + filepath + " already exist."); } std::ofstream out; out.open(filepath); if (!out.is_open()) { throw std::runtime_error("cannot open output file: " + filepath); } for (const auto& section : reader.Sections()) { out << "[" << section << "]\n"; for (const auto& key : reader.Keys(section)) { out << key << "=" << reader.Get(section, key) << "\n"; } } out.close(); } }; } #endif /* __INIWRITER_H__ */
2.config.ini
3.example.cpp
#include <iostream> #include <string> #include <typeinfo> #include <boost/core/demangle.hpp> #include "ini/ini.h" using namespace inih; namespace bc = boost::core; int main() { INIReader r{"./test/fixtures/config.ini"}; const auto& v1 = r.Get<std::string>("section1", "any"); const auto& v2 = r.Get<int>("section1", "any"); const auto& v3 = r.Get<double>("section1", "any"); const auto& v4 = r.GetVector<float>("section2", "any_vec"); const auto& v5{r.GetVector<std::string>("section2", "any_vec")}; /** output * v1 = "1" type: std::string * v2 = 1 type: int * v3 = 1.0 type: double * v4 = [1, 2, 3] type: std::vector<float> * v5 = ["1", "2", "3"] type: std::vector<std::string> */ std::cout << "v1 = " << v1 << ", which is type: " << bc::demangle(typeid(v1).name()) << std::endl; std::cout << "v2 = " << v2 << ", which is type: " << bc::demangle(typeid(v2).name()) << std::endl; std::cout << "v3 = " << v3 << ", which is type: " << bc::demangle(typeid(v3).name()) << std::endl; std::cout << "v4 = "; for (auto& v : v4) std::cout << v << " "; std::cout << ", which is type: " << bc::demangle(typeid(v4).name()) << std::endl; std::cout << "v5 = "; for (auto& v : v5) std::cout << v << " "; std::cout << ", which is type: " << bc::demangle(typeid(v5).name()) << std::endl; // section exist, key not exist r.InsertEntry("section1", "my_custom_key", "hello world"); // section&key not exist r.InsertEntry("new_section", "key1", 5); // Dump ini to file INIWriter::write("output.ini", r); return 0; }
3.使用inipp.h頭文件
3.1 解析算法
section被設(shè)置為空字符串
從文件中讀取每一行并從空白處修剪。
- 如果行為空或以;開始然后什么都沒發(fā)生。
- 否則,如果行以 [ 開頭,則section將更改為 [ 和 ] 之間的字符串。如果行不以 ] 結(jié)尾,則報告錯誤。
- 否則,如果行包含 = 符號,則 = 之前的所有字符都被視為變量, = 之后的所有字符都被視為值。兩者都被修剪。如果變量之前已經(jīng)賦值,則會報告錯誤。否則,將相應(yīng)的賦值添加到該section。
- 否則,該行被報告為錯誤。
3.2 默認(rèn)section算法
將默認(rèn)section中的每個變量插入到其他每個section中,而不覆蓋現(xiàn)有變量。
3.3 Interpolation算法
1.每個section內(nèi)部,遇到的每個variable都被{section:variable}代替
2.每個{section:variable}都被它的值代替
3.重復(fù)上一步,直到無法再進(jìn)行替換,或者直到達(dá)到遞歸深度(默認(rèn)為 10)。
3.4 代碼實(shí)現(xiàn)
1.inipp.h
/* MIT License Copyright (c) 2017-2020 Matthias C. M. Troffaes Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #pragma once #include <algorithm> #include <cctype> #include <cstring> #include <functional> #include <iostream> #include <list> #include <vector> #include <locale> #include <map> #include <memory> #include <sstream> #include <string> namespace inipp { namespace detail { // trim functions based on http://stackoverflow.com/a/217605 template <class CharT> inline void ltrim(std::basic_string<CharT> & s, const std::locale & loc) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [&loc](CharT ch) { return !std::isspace(ch, loc); })); } template <class CharT> inline void rtrim(std::basic_string<CharT> & s, const std::locale & loc) { s.erase(std::find_if(s.rbegin(), s.rend(), [&loc](CharT ch) { return !std::isspace(ch, loc); }).base(), s.end()); } template <class CharT, class UnaryPredicate> inline void rtrim2(std::basic_string<CharT>& s, UnaryPredicate pred) { s.erase(std::find_if(s.begin(), s.end(), pred), s.end()); } // string replacement function based on http://stackoverflow.com/a/3418285 template <class CharT> inline bool replace(std::basic_string<CharT> & str, const std::basic_string<CharT> & from, const std::basic_string<CharT> & to) { auto changed = false; size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::basic_string<CharT>::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); changed = true; } return changed; } } // namespace detail template <typename CharT, typename T> inline bool extract(const std::basic_string<CharT> & value, T & dst) { CharT c; std::basic_istringstream<CharT> is{ value }; T result; if ((is >> std::boolalpha >> result) && !(is >> c)) { dst = result; return true; } else { return false; } } template <typename CharT> inline bool extract(const std::basic_string<CharT> & value, std::basic_string<CharT> & dst) { dst = value; return true; } template <typename CharT, typename T> inline bool get_value(const std::map<std::basic_string<CharT>, std::basic_string<CharT>> & sec, const std::basic_string<CharT> & key, T & dst) { const auto it = sec.find(key); if (it == sec.end()) return false; return extract(it->second, dst); } template <typename CharT, typename T> inline bool get_value(const std::map<std::basic_string<CharT>, std::basic_string<CharT>>& sec, const CharT* key, T& dst) { return get_value(sec, std::basic_string<CharT>(key), dst); } template<class CharT> class Format { public: // used for generating const CharT char_section_start; const CharT char_section_end; const CharT char_assign; const CharT char_comment; // used for parsing virtual bool is_section_start(CharT ch) const { return ch == char_section_start; } virtual bool is_section_end(CharT ch) const { return ch == char_section_end; } virtual bool is_assign(CharT ch) const { return ch == char_assign; } virtual bool is_comment(CharT ch) const { return ch == char_comment; } // used for interpolation const CharT char_interpol; const CharT char_interpol_start; const CharT char_interpol_sep; const CharT char_interpol_end; Format(CharT section_start, CharT section_end, CharT assign, CharT comment, CharT interpol, CharT interpol_start, CharT interpol_sep, CharT interpol_end) : char_section_start(section_start) , char_section_end(section_end) , char_assign(assign) , char_comment(comment) , char_interpol(interpol) , char_interpol_start(interpol_start) , char_interpol_sep(interpol_sep) , char_interpol_end(interpol_end) {} Format() : Format('[', ']', '=', ';', '$', '{', ':', '}') {} const std::basic_string<CharT> local_symbol(const std::basic_string<CharT>& name) const { return char_interpol + (char_interpol_start + name + char_interpol_end); } const std::basic_string<CharT> global_symbol(const std::basic_string<CharT>& sec_name, const std::basic_string<CharT>& name) const { return local_symbol(sec_name + char_interpol_sep + name); } }; template<class CharT> class Ini { public: using String = std::basic_string<CharT>; using Section = std::map<String, String>; using Sections = std::map<String, Section>; Sections sections; std::list<String> errors; std::shared_ptr<Format<CharT>> format; static const int max_interpolation_depth = 10; Ini() : format(std::make_shared<Format<CharT>>()) {}; Ini(std::shared_ptr<Format<CharT>> fmt) : format(fmt) {}; void generate(std::basic_ostream<CharT>& os) const { for (auto const & sec : sections) { os << format->char_section_start << sec.first << format->char_section_end << std::endl; for (auto const & val : sec.second) { os << val.first << format->char_assign << val.second << std::endl; } os << std::endl; } } void parse(std::basic_istream<CharT> & is) { String line; String section; const std::locale loc{"C"}; while (std::getline(is, line)) { detail::ltrim(line, loc); detail::rtrim(line, loc); const auto length = line.length(); if (length > 0) { const auto pos = std::find_if(line.begin(), line.end(), [this](CharT ch) { return format->is_assign(ch); }); const auto & front = line.front(); if (format->is_comment(front)) { } else if (format->is_section_start(front)) { if (format->is_section_end(line.back())) section = line.substr(1, length - 2); else errors.push_back(line); } else if (pos != line.begin() && pos != line.end()) { String variable(line.begin(), pos); String value(pos + 1, line.end()); detail::rtrim(variable, loc); detail::ltrim(value, loc); auto & sec = sections[section]; if (sec.find(variable) == sec.end()) sec.emplace(variable, value); else errors.push_back(line); } else { errors.push_back(line); } } } } void interpolate() { int global_iteration = 0; auto changed = false; // replace each "variable"by"{section:variable}" for (auto & sec : sections) replace_symbols(local_symbols(sec.first, sec.second), sec.second); // replace each "${section:variable}" by its value do { changed = false; const auto syms = global_symbols(); for (auto & sec : sections) changed |= replace_symbols(syms, sec.second); } while (changed && (max_interpolation_depth > global_iteration++)); } void default_section(const Section & sec) { for (auto & sec2 : sections) for (const auto & val : sec) sec2.second.insert(val); } void strip_trailing_comments() { const std::locale loc{ "C" }; for (auto & sec : sections) for (auto & val : sec.second) { detail::rtrim2(val.second, [this](CharT ch) { return format->is_comment(ch); }); detail::rtrim(val.second, loc); } } void clear() { sections.clear(); errors.clear(); } private: using Symbols = std::vector<std::pair<String, String>>; const Symbols local_symbols(const String & sec_name, const Section & sec) const { Symbols result; for (const auto & val : sec) result.emplace_back(format->local_symbol(val.first), format->global_symbol(sec_name, val.first)); return result; } const Symbols global_symbols() const { Symbols result; for (const auto & sec : sections) for (const auto & val : sec.second) result.emplace_back(format->global_symbol(sec.first, val.first), val.second); return result; } bool replace_symbols(const Symbols & syms, Section & sec) const { auto changed = false; for (auto & sym : syms) for (auto & val : sec) changed |= detail::replace(val.second, sym.first, sym.second); return changed; } }; } // namespace inipp
2.example.ini
3.example.cpp
#include <fstream> #include "inipp.h" int main() { inipp::Ini<char> ini; std::ifstream is("example.ini"); ini.parse(is); std::cout << "raw ini file:" << std::endl; ini.generate(std::cout); ini.default_section(ini.sections["DEFAULT"]); ini.interpolate(); std::cout << "ini file after default section and interpolation:" << std::endl; ini.generate(std::cout); int compression_level = -1; inipp::get_value(ini.sections["bitbucket.org"], "CompressionLevel", compression_level); std::cout << "bitbucket.org compression level: " << compression_level << std::endl; return 0; }
到此這篇關(guān)于C++實(shí)現(xiàn)ini文件讀寫的示例代碼的文章就介紹到這了,更多相關(guān)C++讀寫ini文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++/JAVA/C#子類調(diào)用父類函數(shù)情況總結(jié)
今天小編就為大家分享一篇關(guān)于C++/JAVA/C#子類調(diào)用父類函數(shù)情況總結(jié),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03Qt+OpenCV利用幀差法實(shí)現(xiàn)車輛識別
所謂幀差法也就是對連續(xù)圖像幀做差分運(yùn)算,其結(jié)果與定義好的閾值比較,若大于閾值則為運(yùn)動目標(biāo)值為1,否則值為0?。本文將利用幀差法實(shí)現(xiàn)車輛識別,感興趣的可以了解一下2022-08-08C語言菜鳥基礎(chǔ)教程之?dāng)?shù)據(jù)類型
在 C 語言中,數(shù)據(jù)類型指的是用于聲明不同類型的變量或函數(shù)的一個廣泛的系統(tǒng)。變量的類型決定了變量存儲占用的空間,以及如何解釋存儲的位模式。2017-10-10C語言for循環(huán)嵌套for循環(huán)在實(shí)踐題目中應(yīng)用詳解
初學(xué)C語言,常常遇到for循環(huán)中嵌套個for循環(huán),初學(xué)者對于這種形式總是一知半解,這次我就整理了常見的for循環(huán)嵌套for循環(huán)的題目,我們一起爭取一舉拿下這類題。學(xué)廢他們,以后再見到就不怕啦!每天都要學(xué)一點(diǎn)呀。加油,奮斗的我們2022-05-05