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);
180 throw std::runtime_error(
"ini file not found.");
182 throw std::runtime_error(
"memory alloc error");
184 throw std::runtime_error(
"parse error on line no: " +
195 std::set<std::string> retval;
196 for (
const auto& element :
_values) {
197 retval.insert(element.first);
207 std::set<std::string>
Keys(
const std::string& section)
const {
208 const auto& sec = GetSection(section);
209 std::set<std::string> retval;
210 for (
const auto& element : sec) {
211 retval.insert(element.first);
222 std::unordered_map<std::string, std::string>
Get(
223 const std::string& section)
const {
224 return GetSection(section);
235 template <
typename T = std::
string>
236 T
Get(
const std::string& section,
const std::string& name)
const {
237 const auto& sec = GetSection(section);
238 const auto value = sec.find(name);
239 if (value == sec.end()) {
240 throw std::runtime_error(
241 "key '" + name +
"' not found in section '" + section +
"'.");
244 if constexpr (std::is_same_v<T, std::string>) {
245 return value->second;
246 }
else if constexpr (std::is_same_v<T, bool>) {
262 template <
typename T>
263 T
Get(
const std::string& section,
const std::string& name,
264 T&& default_v)
const {
266 return Get<T>(section, name);
267 }
catch (std::runtime_error&) {
268 return std::forward<T>(default_v);
288 template <
typename T = std::
string>
290 const std::string& name)
const {
291 const std::string value =
Get(section, name);
295 while (i < value.size()) {
296 while (i < value.size() && detail::is_space(value[i])) ++i;
298 while (j < value.size() && !detail::is_space(value[j])) ++j;
304 }
catch (std::exception&) {
305 throw std::runtime_error(
"cannot parse value " + value +
321 template <
typename T>
323 const std::string& name,
324 const std::vector<T>& default_v)
const {
327 }
catch (std::runtime_error&) {
339 template <
typename T = std::
string>
340 void InsertEntry(
const std::string& section,
const std::string& name,
343 throw std::runtime_error(
"duplicate key '" + name +
344 "' in section '" + section +
"'.");
355 template <
typename T = std::
string>
356 void InsertEntry(
const std::string& section,
const std::string& name,
357 const std::vector<T>& vs) {
359 throw std::runtime_error(
"duplicate key '" + name +
360 "' in section '" + section +
"'.");
371 template <
typename T = std::
string>
372 void UpdateEntry(
const std::string& section,
const std::string& name,
374 FindEntry(section, name) =
V2String(v);
384 template <
typename T = std::
string>
385 void UpdateEntry(
const std::string& section,
const std::string& name,
386 const std::vector<T>& vs) {
395 std::unordered_map<std::string,
396 std::unordered_map<std::string, std::string>>
400 template <
typename T>
402 if constexpr (std::is_same_v<T, std::string>) {
406 if (!detail::parse_value(s, v)) {
407 throw std::runtime_error(
"cannot parse value '" + s +
418 if (c >=
'A' && c <=
'Z') c +=
'a' -
'A';
420 static const std::unordered_map<std::string, bool> s2b{
421 {
"1",
true}, {
"true",
true}, {
"yes",
true}, {
"on",
true},
422 {
"0",
false}, {
"false",
false}, {
"no",
false}, {
"off",
false},
424 const auto value = s2b.find(s);
425 if (value == s2b.end()) {
426 throw std::runtime_error(
"'" + s +
427 "' is not a valid boolean value.");
429 return value->second;
433 template <
typename T>
435 std::ostringstream ss;
441 template <
typename T>
443 std::ostringstream oss;
444 for (std::size_t i = 0; i < v.size(); ++i) {
445 if (i > 0) oss <<
' ';
452 const std::unordered_map<std::string, std::string>& GetSection(
453 const std::string& section)
const {
454 const auto sec =
_values.find(section);
456 throw std::runtime_error(
"section '" + section +
"' not found.");
461 std::string& FindEntry(
const std::string& section,
462 const std::string& name) {
463 const auto sec =
_values.find(section);
465 const auto value = sec->second.find(name);
466 if (value != sec->second.end()) {
467 return value->second;
470 throw std::runtime_error(
"key '" + name +
"' not exist in section '" +
481 void Parse(std::string_view content) {
482 constexpr std::string_view bom{
"\xEF\xBB\xBF", 3};
483 if (content.substr(0, bom.size()) == bom) {
484 content.remove_prefix(bom.size());
488 std::unordered_map<std::string, std::string>* values =
nullptr;
492 while (!content.empty()) {
494 const auto eol = content.find(
'\n');
495 const auto line = detail::trim(content.substr(0, eol));
496 content.remove_prefix(eol == std::string_view::npos ? content.size()
499 if (line.empty() || line.front() ==
';' || line.front() ==
'#') {
501 }
else if (line.front() ==
'[') {
504 detail::find_char_or_comment(line.substr(1),
"]");
505 if (end != std::string_view::npos && line[end + 1] ==
']') {
506 section.assign(line.data() + 1, end);
515 const auto sep = detail::find_char_or_comment(line,
"=:");
516 if (sep == std::string_view::npos || line[sep] ==
';') {
521 const auto name = detail::rtrim(line.substr(0, sep));
522 auto value = line.substr(sep + 1);
523 const auto comment = detail::find_char_or_comment(value, {});
524 if (comment != std::string_view::npos) {
525 value = value.substr(0, comment);
527 value = detail::trim(value);
529 if (values ==
nullptr) {
532 if (!values->emplace(std::string(name), std::string(value))
534 throw std::runtime_error(
"duplicate key '" +
536 "' in section '" + section +
"'.");
557 inline static void write(
const std::string& filepath,
559 const bool overwrite =
false) {
560 if (!overwrite && std::ifstream{filepath}) {
561 throw std::runtime_error(
"file: " + filepath +
" already exists.");
563 std::ofstream out{filepath};
564 if (!out.is_open()) {
565 throw std::runtime_error(
"cannot open output file: " + filepath);
567 for (
const auto& section : reader.
Sections()) {
568 out <<
"[" << section <<
"]\n";
569 for (
const auto& key : reader.
Keys(section)) {
570 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:263
T Converter(const std::string &s) const
Parse s as a T; throws std::runtime_error on failure.
Definition ini.h:401
std::unordered_map< std::string, std::unordered_map< std::string, std::string > > _values
Parsed content, as _values[section][name] = value.
Definition ini.h:397
int _error
Parse result: 0 on success, -1 on file open error, otherwise the number of the first faulty line.
Definition ini.h:393
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:356
std::string V2String(const T &v) const
Serialize a value with operator<<.
Definition ini.h:434
int ParseError() const
Return the result of the parse, i.e., 0 on success.
Definition ini.h:175
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:222
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:340
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:372
std::string Vec2String(const std::vector< T > &v) const
Serialize a vector as space-separated values.
Definition ini.h:442
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:194
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:289
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:236
static INIReader FromString(std::string_view content)
Construct an INIReader object from in-memory ini content.
Definition ini.h:164
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:385
std::set< std::string > Keys(const std::string §ion) const
Return the list of keys in the given section.
Definition ini.h:207
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:416
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:322
Write the contents of an INIReader to an ini file.
Definition ini.h:546
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:557
Yet another .ini parser for modern c++ (made for cpp17), Project page: https://github....
Definition ini.h:32