Console Library 8.0.0
A header-only library that makes C++ simple
Loading...
Searching...
No Matches
config.h
Go to the documentation of this file.
1
9
10/*
11Copyright (c) 2026 MrXie1109
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in all
21copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29SOFTWARE.
30*/
31
32#pragma once
33#include <map>
34
35#include "../container/maybe.h"
36#include "../io/file.h"
37#include "strpp.h"
38
39namespace console {
46 template <class CharT = char>
48 using string_type = std::basic_string<CharT>;
49
50 std::map<string_type, std::map<string_type, string_type>> data_;
51
52 public:
54 BasicINIConfig() = default;
55
60 BasicINIConfig(const std::string &filename) { load(Path(filename)); }
61
66 BasicINIConfig(const Path &filename) { load(filename); }
67
73 void load(const Path &filename) {
74 std::fstream fs = filename.stream();
75 fs >> *this;
76 }
77
83 void save(const std::string &filename) { save(Path(filename)); }
84
90 void save(const Path &filename) const {
91 std::ofstream fout(filename.str(), std::ios::trunc);
92 if (!fout.is_open())
93 throw FileError("Cannot Open File \"" + filename.str() + '"');
94 // 用 narrow 流输出,通过窄字符串中转
95 std::ostringstream oss;
96 write_to_stream(oss);
97 fout << oss.str();
98 if (!fout.good())
99 throw FileError(
100 "The Stream of \"" + filename.str() + "\" is Not Good");
101 }
102
103 private:
104 void write_to_stream(std::ostream &os) const {
105 for (const auto &p : data_) {
106 if (!p.first.empty())
107 os << '[' << to_narrow(p.first) << ']' << '\n';
108 for (const auto &kv : p.second)
109 os << to_narrow(kv.first) << " = " << to_narrow(kv.second)
110 << '\n';
111 os << '\n';
112 }
113 }
114
115 static std::string to_narrow(const string_type &s) {
116 return std::string(s.begin(), s.end());
117 }
118
119 void read_from_stream(std::istream &is) {
120 std::string line;
121 string_type current_section;
122 while (std::getline(is, line)) {
123 string_type trimmed_line
124 = trim(string_type(line.begin(), line.end()));
125 if (trimmed_line.empty() || trimmed_line[0] == CharT(';')
126 || trimmed_line[0] == CharT('#'))
127 continue;
128 if (trimmed_line.front() == CharT('[')
129 && trimmed_line.back() == CharT(']'))
130 current_section
131 = trim(trimmed_line.substr(1, trimmed_line.size() - 2));
132 else {
133 auto pos = trimmed_line.find(CharT('='));
134 if (pos != string_type::npos) {
135 string_type key = trim(trimmed_line.substr(0, pos));
136 string_type value = trim(trimmed_line.substr(pos + 1));
137 data_[current_section][key] = value;
138 }
139 }
140 }
141 }
142
143 public:
147 friend std::basic_ostream<CharT> &operator<<(
148 std::basic_ostream<CharT> &os, const BasicINIConfig &config) {
149 for (const auto &p : config.data_) {
150 if (!p.first.empty())
151 os << CharT('[') << p.first << CharT(']') << CharT('\n');
152 for (const auto &kv : p.second)
153 os << kv.first << " = " << kv.second << CharT('\n');
154 os << CharT('\n');
155 }
156 return os;
157 }
158
162 friend std::basic_istream<CharT> &
163 operator>>(std::basic_istream<CharT> &is, BasicINIConfig &config) {
164 string_type line;
165 string_type current_section;
166 while (std::getline(is, line)) {
167 string_type trimmed_line = trim(line);
168 if (trimmed_line.empty() || trimmed_line[0] == CharT(';')
169 || trimmed_line[0] == CharT('#'))
170 continue;
171 if (trimmed_line.front() == CharT('[')
172 && trimmed_line.back() == CharT(']'))
173 current_section
174 = trim(trimmed_line.substr(1, trimmed_line.size() - 2));
175 else {
176 auto pos = trimmed_line.find(CharT('='));
177 if (pos != string_type::npos) {
178 string_type key = trim(trimmed_line.substr(0, pos));
179 string_type value = trim(trimmed_line.substr(pos + 1));
180 config.data_[current_section][key] = value;
181 }
182 }
183 }
184 return is;
185 }
186
190 class Item {
192
193 public:
194 Item(const string_type &s) : str_(s) {}
195
196 operator string_type() const { return str_; }
197
198 operator bool() const {
200 for (auto &c : lower) c = std::tolower(c, std::locale{});
201 if (lower
202 == string_type{//
203 CharT('t'),
204 CharT('r'),
205 CharT('u'),
206 CharT('e')}
207 || lower == string_type{CharT('1')}
208 || lower == string_type{CharT('y'), CharT('e'), CharT('s')}
209 || lower == string_type{CharT('o'), CharT('n')})
210 return true;
211 if (lower
212 == string_type{//
213 CharT('f'),
214 CharT('a'),
215 CharT('l'),
216 CharT('s'),
217 CharT('e')}
218 || lower == string_type{CharT('0')}
219 || lower == string_type{CharT('n'), CharT('o')}
220 || lower == string_type{CharT('o'), CharT('f'), CharT('f')})
221 return false;
222 throw TypeError(
223 "Failed to Convert \"" + to_narrow(str_) + "\" to bool");
224 }
225
226 template <class T>
227 operator T() const {
228 std::basic_istringstream<CharT> iss(str_);
229 T value;
230 iss >> value;
231 if (iss.fail())
232 throw TypeError("Failed to Convert \"" + to_narrow(str_)
233 + "\" to Target Type");
234 return value;
235 }
236 };
237
244 Item get(const string_type &section_and_key) const {
245 auto pr = partition(section_and_key, string_type(1, CharT('.')));
246 if (pr.middle.empty())
247 throw IndexError("Invalid Section and Key Format: \""
248 + to_narrow(section_and_key) + '"');
249 auto section = pr.left;
250 auto key = pr.right;
251 auto sec_it = data_.find(section);
252 if (sec_it == data_.end())
253 throw IndexError(
254 "Section \"" + to_narrow(section) + "\" Not Found");
255 auto key_it = sec_it->second.find(key);
256 if (key_it == sec_it->second.end())
257 throw IndexError("Key \"" + to_narrow(key)
258 + "\" Not Found in Section \""
259 + to_narrow(section) + '"');
260 return Item(key_it->second);
261 }
262
263 template <class T>
264 T get(const string_type &section_and_key) const {
265 return static_cast<T>(get(section_and_key));
266 }
267
275 template <class T>
276 T
277 get(const string_type &section_and_key, const T &default_value) const {
278 auto pr = partition(section_and_key, string_type(1, CharT('.')));
279 if (pr.middle.empty()) return default_value;
280 auto section = pr.left;
281 auto key = pr.right;
282 auto sec_it = data_.find(section);
283 if (sec_it == data_.end()) return default_value;
284 auto key_it = sec_it->second.find(key);
285 if (key_it == sec_it->second.end()) return default_value;
286 return T(Item(key_it->second));
287 }
288
296 void set(const string_type &section_and_key, const string_type &value) {
297 auto pr = partition(section_and_key, string_type(1, CharT('.')));
298 if (pr.middle.empty())
299 throw IndexError("Invalid Section and Key Format: \""
300 + to_narrow(section_and_key) + '"');
301 auto section = pr.left;
302 auto key = pr.right;
303 data_[section][key] = value;
304 }
305
311 bool has(const string_type &section_and_key) const {
312 auto pr = partition(section_and_key, string_type(1, CharT('.')));
313 if (pr.middle.empty()) {
314 auto section = pr.left;
315 return data_.find(section) != data_.end();
316 } else {
317 auto section = pr.left;
318 auto key = pr.right;
319 auto sec_it = data_.find(section);
320 if (sec_it == data_.end()) return false;
321 return sec_it->second.find(key) != sec_it->second.end();
322 }
323 }
324
330 bool remove(const string_type &section_and_key) {
331 auto pr = partition(section_and_key, string_type(1, CharT('.')));
332 if (pr.middle.empty()) {
333 auto section = pr.left;
334 return data_.erase(section) > 0;
335 } else {
336 auto section = pr.left;
337 auto key = pr.right;
338 auto sec_it = data_.find(section);
339 if (sec_it == data_.end()) return false;
340 return sec_it->second.erase(key) > 0;
341 }
342 }
343
347 const std::map<string_type, std::map<string_type, string_type>> &
348 data() const {
349 return data_;
350 }
351 };
352
355
358
359}
Item(const string_type &s)
Definition config.h:194
string_type str_
Definition config.h:191
INI 配置文件操作类。
Definition config.h:47
friend std::basic_ostream< CharT > & operator<<(std::basic_ostream< CharT > &os, const BasicINIConfig &config)
配置对象的输出流运算符,按 INI 格式输出配置内容。
Definition config.h:147
void set(const string_type &section_and_key, const string_type &value)
设置配置项的值。
Definition config.h:296
bool has(const string_type &section_and_key) const
检查配置项或节是否存在。
Definition config.h:311
std::map< string_type, std::map< string_type, string_type > > data_
Definition config.h:50
void save(const Path &filename) const
将配置保存到文件。
Definition config.h:90
T get(const string_type &section_and_key, const T &default_value) const
获取配置项的值,若不存在则返回默认值。
Definition config.h:277
void read_from_stream(std::istream &is)
Definition config.h:119
void load(const Path &filename)
Definition config.h:73
bool remove(const string_type &section_and_key)
删除配置项或节。
Definition config.h:330
static std::string to_narrow(const string_type &s)
Definition config.h:115
T get(const string_type &section_and_key) const
Definition config.h:264
const std::map< string_type, std::map< string_type, string_type > > & data() const
获取原始数据结构的只读引用。
Definition config.h:348
BasicINIConfig(const Path &filename)
从指定文件加载配置的构造函数。
Definition config.h:66
std::basic_string< CharT > string_type
Definition config.h:48
void write_to_stream(std::ostream &os) const
Definition config.h:104
BasicINIConfig(const std::string &filename)
从指定文件加载配置的构造函数。
Definition config.h:60
BasicINIConfig()=default
默认构造函数,创建空的配置对象。
void save(const std::string &filename)
将配置保存到文件。
Definition config.h:83
friend std::basic_istream< CharT > & operator>>(std::basic_istream< CharT > &is, BasicINIConfig &config)
配置对象的输入流运算符,按 INI 格式输入配置内容。
Definition config.h:163
Item get(const string_type &section_and_key) const
获取配置项的值。
Definition config.h:244
std::fstream stream(std::ios_base::openmode mode=std::ios_base::in|std::ios_base::out) const
以指定模式打开文件流。
Definition file.h:350
const string_type & str() const
获取文件路径字符串。
Definition file.h:343
FileError(const std::string &msg)
构造 FileError。
Definition csexc.h:87
IndexError(const std::string &msg)
构造 IndexError。
Definition csexc.h:153
Item()
默认构造一个空 Item(ptr 为 nullptr)。
Definition box.h:126
TypeError(const std::string &msg)
构造 TypeError。
Definition csexc.h:102
提供跨平台的文件路径封装和文件 I/O 操作。
std::basic_string< CharT, Traits, Allocator > trim(std::basic_string< CharT, Traits, Allocator > str)
移除字符串两侧的空白字符。
Definition strpp.h:102
BasicPartitionResult< CharT, Traits, Allocator > partition(const std::basic_string< CharT, Traits, Allocator > &text, const std::basic_string< CharT, Traits, Allocator > &sep)
在字符串中查找第一个分隔符,并返回分隔符之前、分隔符本身、分隔符之后的三部分。
Definition strpp.h:301
std::basic_string< CharT, Traits, Allocator > lower(std::basic_string< CharT, Traits, Allocator > str, const std::locale &loc=std::locale{})
将字符串转换为小写。
Definition strpp.h:222
提供类似 std::optional 的可选值容器 Maybe,支持空状态(nothing)。
本库所有组件所在的顶层命名空间。
@ T
Definition kb.h:99
BasicINIConfig< wchar_t > WINIConfig
BasicINIConfig<wchar_t> 的类型别名。
Definition config.h:357
BasicPath< char > Path
窄字符路径类型。
Definition file.h:357
BasicINIConfig< char > INIConfig
BasicINIConfig<char> 的类型别名。
Definition config.h:354
提供字符串处理工具函数和格式化类。