介绍
一般的ini配置文件由节、键、值组成。
【参数】(键=值),例如 :key=value;
【节】:所有的参数都是以节(section)为单位结合在一起的。所有的section名称都是独占一行,并且section名字都被方括号包围着([XXX])。在section声明后的所有parameters都属于该section。
例如:[section1]
所以一个包含节,键,值的简单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
#ifndef __INI_H__
#define __INI_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
typedef char* (*ini_reader)(char* str, int num, void* stream);
int ini_parse(const char* filename, ini_handler handler, void* user);
int ini_parse_file(FILE* file, ini_handler handler, void* user);
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
#ifdef __cplusplus
}
#endif
#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
inline static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
inline static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
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;
}
inline static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size);
dest[size - 1] = '\0';
return dest;
}
inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
#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
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 == '#') {
}
#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
if (!handler(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
error = lineno;
}
}
else if (*start) {
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);
strncpy0(prev_name, name, sizeof(prev_name));
if (!handler(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
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
#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头文件
现代 c++ 的另一个 .ini
解析器(为 cpp17
制作),灵感来自 inih 并进行了扩展。
1.ini.h
#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 int (*ini_handler)(void* user, const char* section, const char* name,
const char* value);
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 ";"
inline static char* rstrip(char* s) {
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p))) *p = '\0';
return s;
}
inline static char* lskip(const char* s) {
while (*s && isspace((unsigned char)(*s))) s++;
return (char*)s;
}
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;
}
inline static char* strncpy0(char* dest, const char* src, size_t size) {
strncpy(dest, src, size - 1);
dest[size - 1] = '\0';
return dest;
}
inline int ini_parse_stream(ini_reader reader, void* stream,
ini_handler handler, void* user) {
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
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)) {
} else if (*start == '[') {
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
} else if (!error) {
error = lineno;
}
} else if (*start) {
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);
strncpy0(prev_name, name, sizeof(prev_name));
if (!HANDLER(user, section, name, value) && !error)
error = lineno;
} else if (!error) {
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
#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;
}
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;
}
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;
}
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);
};
}
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;
}
}
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>.");
}
}
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
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")};
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被设置为空字符串
从文件中读取每一行并从空白处修剪。
- 如果行为空或以;开始然后什么都没发生。
- 否则,如果行以 [ 开头,则section将更改为 [ 和 ] 之间的字符串。如果行不以 ] 结尾,则报告错误。
- 否则,如果行包含 = 符号,则 = 之前的所有字符都被视为变量, = 之后的所有字符都被视为值。两者都被修剪。如果变量之前已经赋值,则会报告错误。否则,将相应的赋值添加到该section。
- 否则,该行被报告为错误。
3.2 默认section算法
将默认section中的每个变量插入到其他每个section中,而不覆盖现有变量。
3.3 Interpolation算法
1.每个section内部,遇到的每个variable都被{section:variable}代替
2.每个{section:variable}都被它的值代替
3.重复上一步,直到无法再进行替换,或者直到达到递归深度(默认为 10)。
3.4 代码实现
1.inipp.h
#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;
}
到此这篇关于C++实现ini文件读写的示例代码的文章就介绍到这了,更多相关C++读写ini文件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!