18#include <unordered_map>
22#if defined(__has_include)
23#if __has_include(<charconv>)
25#define INI_CPP_HAS_CHARCONV 1
26#if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L
27#define INI_CPP_HAS_FLOAT_CHARCONV 1
37inline constexpr bool is_space(
char c)
noexcept {
38 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f' ||
42inline std::string_view ltrim(std::string_view s)
noexcept {
43 while (!s.empty() && is_space(s.front())) s.remove_prefix(1);
47inline std::string_view rtrim(std::string_view s)
noexcept {
48 while (!s.empty() && is_space(s.back())) s.remove_suffix(1);
52inline std::string_view trim(std::string_view s)
noexcept {
53 return ltrim(rtrim(s));
58inline std::size_t find_char_or_comment(std::string_view s,
59 std::string_view chars)
noexcept {
60 bool was_space =
false;
61 for (std::size_t i = 0; i < s.size(); ++i) {
63 if (chars.find(c) != std::string_view::npos ||
64 (was_space && c ==
';')) {
67 was_space = is_space(c);
69 return std::string_view::npos;
75inline constexpr bool use_charconv =
76#if defined(INI_CPP_HAS_FLOAT_CHARCONV)
77 std::is_floating_point_v<T> ||
79 (std::is_integral_v<T> && !std::is_same_v<T, bool> &&
80 !std::is_same_v<T, char> && !std::is_same_v<T, signed char> &&
81 !std::is_same_v<T, unsigned char> && !std::is_same_v<T, wchar_t> &&
82 !std::is_same_v<T, char16_t> && !std::is_same_v<T, char32_t>);
87inline bool parse_value(
const std::string& s, T& v) {
88#if defined(INI_CPP_HAS_CHARCONV)
89 if constexpr (use_charconv<T>) {
90 const char* first = s.data();
91 const char*
const last = s.data() + s.size();
92 while (first != last && is_space(*first)) ++first;
94 if (last - first > 1 && *first ==
'+' &&
95 ((first[1] >=
'0' && first[1] <=
'9') || first[1] ==
'.')) {
98 return std::from_chars(first, last, v).ec == std::errc{};
102 std::istringstream in{s};
124 std::ifstream in{filename, std::ios::in | std::ios::binary};
131 in.seekg(0, std::ios::end);
132 const auto size = in.tellg();
134 content.resize(
static_cast<std::size_t
>(size));
135 in.seekg(0, std::ios::beg);
136 in.read(&content[0], size);
151 while ((n = std::fread(buf, 1,
sizeof(buf), file)) > 0) {
152 content.append(buf, n);
167 throw std::runtime_error(
"ini file not found.");
169 throw std::runtime_error(
"memory alloc error");
171 throw std::runtime_error(
"parse error on line no: " +
182 std::set<std::string> retval;
183 for (
const auto& element :
_values) {
184 retval.insert(element.first);
194 std::set<std::string>
Keys(
const std::string& section)
const {
195 const auto& sec = GetSection(section);
196 std::set<std::string> retval;
197 for (
const auto& element : sec) {
198 retval.insert(element.first);
209 std::unordered_map<std::string, std::string>
Get(
210 const std::string& section)
const {
211 return GetSection(section);
222 template <
typename T = std::
string>
223 T
Get(
const std::string& section,
const std::string& name)
const {
224 const auto& sec = GetSection(section);
225 const auto value = sec.find(name);
226 if (value == sec.end()) {
227 throw std::runtime_error(
228 "key '" + name +
"' not found in section '" + section +
"'.");
231 if constexpr (std::is_same_v<T, std::string>) {
232 return value->second;
233 }
else if constexpr (std::is_same_v<T, bool>) {
249 template <
typename T>
250 T
Get(
const std::string& section,
const std::string& name,
251 T&& default_v)
const {
253 return Get<T>(section, name);
254 }
catch (std::runtime_error&) {
255 return std::forward<T>(default_v);
275 template <
typename T = std::
string>
277 const std::string& name)
const {
278 const std::string value =
Get(section, name);
282 while (i < value.size()) {
283 while (i < value.size() && detail::is_space(value[i])) ++i;
285 while (j < value.size() && !detail::is_space(value[j])) ++j;
291 }
catch (std::exception&) {
292 throw std::runtime_error(
"cannot parse value " + value +
308 template <
typename T>
310 const std::string& name,
311 const std::vector<T>& default_v)
const {
314 }
catch (std::runtime_error&) {
326 template <
typename T = std::
string>
327 void InsertEntry(
const std::string& section,
const std::string& name,
330 throw std::runtime_error(
"duplicate key '" + name +
331 "' in section '" + section +
"'.");
342 template <
typename T = std::
string>
343 void InsertEntry(
const std::string& section,
const std::string& name,
344 const std::vector<T>& vs) {
346 throw std::runtime_error(
"duplicate key '" + name +
347 "' in section '" + section +
"'.");
358 template <
typename T = std::
string>
359 void UpdateEntry(
const std::string& section,
const std::string& name,
361 FindEntry(section, name) =
V2String(v);
371 template <
typename T = std::
string>
372 void UpdateEntry(
const std::string& section,
const std::string& name,
373 const std::vector<T>& vs) {
382 std::unordered_map<std::string,
383 std::unordered_map<std::string, std::string>>
387 template <
typename T>
389 if constexpr (std::is_same_v<T, std::string>) {
393 if (!detail::parse_value(s, v)) {
394 throw std::runtime_error(
"cannot parse value '" + s +
405 if (c >=
'A' && c <=
'Z') c +=
'a' -
'A';
407 static const std::unordered_map<std::string, bool> s2b{
408 {
"1",
true}, {
"true",
true}, {
"yes",
true}, {
"on",
true},
409 {
"0",
false}, {
"false",
false}, {
"no",
false}, {
"off",
false},
411 const auto value = s2b.find(s);
412 if (value == s2b.end()) {
413 throw std::runtime_error(
"'" + s +
414 "' is not a valid boolean value.");
416 return value->second;
420 template <
typename T>
422 std::ostringstream ss;
428 template <
typename T>
430 std::ostringstream oss;
431 for (std::size_t i = 0; i < v.size(); ++i) {
432 if (i > 0) oss <<
' ';
439 const std::unordered_map<std::string, std::string>& GetSection(
440 const std::string& section)
const {
441 const auto sec =
_values.find(section);
443 throw std::runtime_error(
"section '" + section +
"' not found.");
448 std::string& FindEntry(
const std::string& section,
449 const std::string& name) {
450 const auto sec =
_values.find(section);
452 const auto value = sec->second.find(name);
453 if (value != sec->second.end()) {
454 return value->second;
457 throw std::runtime_error(
"key '" + name +
"' not exist in section '" +
468 void Parse(std::string_view content) {
469 constexpr std::string_view bom{
"\xEF\xBB\xBF", 3};
470 if (content.substr(0, bom.size()) == bom) {
471 content.remove_prefix(bom.size());
475 std::unordered_map<std::string, std::string>* values =
nullptr;
479 while (!content.empty()) {
481 const auto eol = content.find(
'\n');
482 const auto line = detail::trim(content.substr(0, eol));
483 content.remove_prefix(eol == std::string_view::npos ? content.size()
486 if (line.empty() || line.front() ==
';' || line.front() ==
'#') {
488 }
else if (line.front() ==
'[') {
491 detail::find_char_or_comment(line.substr(1),
"]");
492 if (end != std::string_view::npos && line[end + 1] ==
']') {
493 section.assign(line.data() + 1, end);
502 const auto sep = detail::find_char_or_comment(line,
"=:");
503 if (sep == std::string_view::npos || line[sep] ==
';') {
508 const auto name = detail::rtrim(line.substr(0, sep));
509 auto value = line.substr(sep + 1);
510 const auto comment = detail::find_char_or_comment(value, {});
511 if (comment != std::string_view::npos) {
512 value = value.substr(0, comment);
514 value = detail::trim(value);
516 if (values ==
nullptr) {
519 if (!values->emplace(std::string(name), std::string(value))
521 throw std::runtime_error(
"duplicate key '" +
523 "' in section '" + section +
"'.");
544 inline static void write(
const std::string& filepath,
546 const bool overwrite =
false) {
547 if (!overwrite && std::ifstream{filepath}) {
548 throw std::runtime_error(
"file: " + filepath +
" already exists.");
550 std::ofstream out{filepath};
551 if (!out.is_open()) {
552 throw std::runtime_error(
"cannot open output file: " + filepath);
554 for (
const auto& section : reader.
Sections()) {
555 out <<
"[" << section <<
"]\n";
556 for (
const auto& key : reader.
Keys(section)) {
557 out << key <<
"=" << reader.
Get(section, key) <<
"\n";
Read an INI file into easy-to-access name/value pairs.
Definition ini.h:113
T Get(const std::string §ion, const std::string &name, T &&default_v) const
Return the value of the given key in the given section, return default if not found.
Definition ini.h:250
T Converter(const std::string &s) const
Parse s as a T; throws std::runtime_error on failure.
Definition ini.h:388
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > _values
Parsed content, as _values[section][name] = value.
Definition ini.h:384
int _error
Parse result: 0 on success, -1 on file open error, otherwise the number of the first faulty line.
Definition ini.h:380
void InsertEntry(const std::string §ion, const std::string &name, const std::vector< T > &vs)
Insert a vector of values into the INI file.
Definition ini.h:343
std::string V2String(const T &v) const
Serialize a value with operator<<.
Definition ini.h:421
int ParseError() const
Return the result of the parse, i.e., 0 on success.
Definition ini.h:162
std::unordered_map< std::string, std::string > Get(const std::string §ion) const
Get the map representing the values in a section of the INI file.
Definition ini.h:209
void InsertEntry(const std::string §ion, const std::string &name, const T &v)
Insert a key-value pair into the INI file.
Definition ini.h:327
void UpdateEntry(const std::string §ion, const std::string &name, const T &v)
Update a key-value pair in the INI file.
Definition ini.h:359
std::string Vec2String(const std::vector< T > &v) const
Serialize a vector as space-separated values.
Definition ini.h:429
INIReader(std::FILE *file)
Construct an INIReader object from a file pointer.
Definition ini.h:147
std::set< std::string > Sections() const
Return the list of sections found in ini file.
Definition ini.h:181
std::vector< T > GetVector(const std::string §ion, const std::string &name) const
Return the value array of the given key in the given section.
Definition ini.h:276
INIReader(const std::string &filename)
Construct an INIReader object from a file name.
Definition ini.h:123
T Get(const std::string §ion, const std::string &name) const
Return the value of the given key in the given section.
Definition ini.h:223
void UpdateEntry(const std::string §ion, const std::string &name, const std::vector< T > &vs)
Update a vector of values in the INI file.
Definition ini.h:372
std::set< std::string > Keys(const std::string §ion) const
Return the list of keys in the given section.
Definition ini.h:194
bool BoolConverter(std::string s) const
Parse a boolean token: 1/0/true/false/yes/no/on/off, case-insensitive; throws std::runtime_error on a...
Definition ini.h:403
std::vector< T > GetVector(const std::string §ion, const std::string &name, const std::vector< T > &default_v) const
Return the value array of the given key in the given section, return default if not found.
Definition ini.h:309
Write the contents of an INIReader to an ini file.
Definition ini.h:533
static void write(const std::string &filepath, const INIReader &reader, const bool overwrite=false)
Write the contents of an INI file to a new file.
Definition ini.h:544
Yet another .ini parser for modern c++ (made for cpp17), Project page: https://github....
Definition ini.h:32