├── .gitignore ├── LICENSE ├── README.md ├── ini_loader.cpp ├── ini_loader.h └── sample ├── config.ini ├── sample1.cpp ├── sample2.cpp └── test.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 OWenT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libiniloader 2 | ============ 3 | 4 | libiniloader 是一个简洁的ini配置读取库,仅需要两个文件。 5 | 6 | 7 | 注意 8 | ------ 9 | 本库读取ini的方式和标准ini有略微不同,主要区别如下: 10 | 11 | 1. 支持Section 12 | 2. Secion支持父子关系,即 [ A : B : C ] 语法 13 | 3. 支持多重父子关系,如 C.B.A = d 14 | 4. 支持字符串转义,其中以'包裹的不进行转义,以"包裹的可进行转义,如 C.B.A = "Hello \r\n World!\0" 15 | 5. 配置的Key名称不能包含引号('和")、点(.)、冒号(:)和方括号([]) 16 | 6. 配置的Key名称区分大小写 17 | 7. #符号也将是做行注释,与 ; 等价 18 | 8. 支持解析时间长度(dump_to到\:\:util\:\:config\:\:duration_value类型),单位如下 19 | + 纳秒: ns, nanosecond, nanoseconds 如: 100ns 20 | + 微秒: us, microsecond, microseconds 如: 100us 21 | + 毫秒: ms, millisecond, milliseconds 如: 100ms 22 | + 秒: s, sec, second, seconds 如: 100s 23 | + 分: m, minute, minutes 如: 100m 24 | + 时: h, hour, hours 如: 100h 25 | + 天: d, day, days 如: 100d 26 | + 周: w, week, weeks 如: 100w 27 | 28 | 29 | 使用方法 30 | ------ 31 | 使用libiniloader非常简单,只需要包含头文件 ini_loader.h 并编译源文件 ini_loader.cpp 即可 32 | 33 | 其他文档详见 [ini_loader.h](ini_loader.h) 文件内注释 34 | 35 | 使用示例 36 | ------ 37 | ini文件:(test.ini) 38 | 39 | ```ini 40 | a.b.c1 = 123 # 整数 41 | a.b.c2 = 1.23 ; 小数 42 | 43 | [a : b:c:d] 44 | e.f1 = "123\0456" ; 可转义字符串 45 | 46 | e.f2 = '123\0456'; 不可转义字符串 47 | 48 | [d . c:b.a] 49 | e.f3 = 123456789 50 | 51 | [a] 52 | b.c3 = 带空格 的 字符 串 53 | 54 | arr = 1 55 | arr = 2 56 | arr = 3 57 | arr = 一坨屎 58 | arr = /usr/local/gcc-4.8.2 59 | 60 | 61 | bool = true 62 | bool = false 63 | bool = yes 64 | bool = no 65 | bool = enable 66 | bool = disable 67 | bool = 1 68 | bool = 0 69 | ``` 70 | 71 | 测试代码: 72 | ```cpp 73 | #include 74 | #include 75 | #include 76 | #include "ini_loader.h" 77 | 78 | 79 | int main(){ 80 | util::config::ini_loader cfg_loader; 81 | 82 | cfg_loader.load_file("test.ini"); 83 | 84 | 85 | // 转储整数 86 | { 87 | int t1 = 9999, t2 = 9999; 88 | cfg_loader.dump_to("a.b.c1", t1); 89 | cfg_loader.dump_to("a.b.c4", t2, true); 90 | printf("a.b.c1 = %d\na.b.c4 = %d\n", t1, t2); 91 | } 92 | 93 | // 转储浮点数 94 | { 95 | float t1 = 0.0; 96 | cfg_loader.dump_to("a.b.c2", t1); 97 | printf("a.b.c2 = %f\n", t1); 98 | } 99 | 100 | // 转储字符串 101 | { 102 | char t1[32] = {0}; 103 | std::string t2, t3 = "0000000000000000"; 104 | std::string t4, t5; 105 | 106 | cfg_loader.dump_to("d.c.b.a.e.f1", t2); // 字符串 107 | cfg_loader.dump_to("d.c.b.a.e.f1", t3.begin(), t3.end()); // 字符串迭代器 108 | cfg_loader.dump_to("d.c.b.a.e.f2", t1); // 字符串 109 | cfg_loader.dump_to("d.c.b.a.e.f2", t1 + 16, t1 + 32); // 字符串指针迭代器 110 | cfg_loader.dump_to("a.b.c3", t4); // 带不可打印字符的字符串 111 | cfg_loader.dump_to("d.c.b.a.e.f3", t5); // 字符串 - Section使用.分割层级 112 | 113 | printf("len(t2) = %d\nlen(t3) = %d\n", (int)t2.size(), (int)t3.size()); 114 | printf("d.c.b.a.e.f2 = %s\n", t1); 115 | printf("d.c.b.a.e.f2 = %s(+16)\n", t1 + 16); 116 | printf("d.c.b.a.e.f3 = %s\n", t5.c_str()); 117 | printf("a.b.c3 = %s\n", t4.c_str()); 118 | } 119 | 120 | // 转储到 vector 121 | { 122 | std::vector t1; 123 | std::list t2; 124 | cfg_loader.dump_to("a.arr", t1); 125 | cfg_loader.dump_to("a.bool", t2); 126 | 127 | for (size_t i = 0; i < t1.size(); ++i) { 128 | printf("t1[%d] = %s\n", (int)i, t1[i].c_str()); 129 | } 130 | 131 | size_t index = 0; 132 | for (std::list::iterator iter = t2.begin(); iter != t2.end(); ++iter) { 133 | printf("t2[%d] = %s\n", (int) index ++, (*iter) ? "true" : "false"); 134 | } 135 | 136 | } 137 | 138 | // 转储时间周期 139 | { 140 | util::config::duration_value dur; 141 | cfg_loader.dump_to("system.interval_ns", dur, true); 142 | printf("system.interval_ns: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 143 | cfg_loader.dump_to("system.interval_us", dur, true); 144 | printf("system.interval_us: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 145 | cfg_loader.dump_to("system.interval_ms", dur, true); 146 | printf("system.interval_ms: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 147 | cfg_loader.dump_to("system.interval_s", dur, true); 148 | printf("system.interval_s: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 149 | cfg_loader.dump_to("system.interval_m", dur, true); 150 | printf("system.interval_m: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 151 | cfg_loader.dump_to("system.interval_h", dur, true); 152 | printf("system.interval_h: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 153 | cfg_loader.dump_to("system.interval_d", dur, true); 154 | printf("system.interval_d: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 155 | cfg_loader.dump_to("system.interval_w", dur, true); 156 | printf("system.interval_w: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 157 | } 158 | 159 | return 0; 160 | } 161 | ``` 162 | 163 | LICENSE 164 | ------ 165 | MIT License 166 | 167 | 168 | 联系作者 169 | ------ 170 | E-Mail: [owt5008137@live.com](mailto:owt5008137@live.com) | [admin@owent.net](mailto:admin@owent.net) 171 | 172 | QQ: 493749449 (请注明 github-libiniloader) 173 | -------------------------------------------------------------------------------- /ini_loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #include "ini_loader.h" 8 | 9 | namespace util { 10 | namespace config { 11 | namespace detail { 12 | typedef const char *const_cstring; 13 | template 14 | T tolower(T c) { 15 | if (c >= 'A' && c <= 'Z') { 16 | return c - 'A' + 'a'; 17 | } 18 | 19 | return c; 20 | } 21 | template 22 | T str2int(const_cstring str, const_cstring *left = NULL) { 23 | T out = 0; 24 | if (NULL == str || !(*str)) { 25 | if (NULL != left) { 26 | *left = str; 27 | } 28 | 29 | return out; 30 | } 31 | 32 | // negative 33 | bool is_negative = false; 34 | while (*str && *str == '-') { 35 | is_negative = !is_negative; 36 | ++str; 37 | } 38 | 39 | if (!(*str)) { 40 | if (NULL != left) { 41 | *left = str; 42 | } 43 | 44 | return out; 45 | } 46 | 47 | size_t i; 48 | if ('0' == str[0] && 'x' == str[1]) { // hex 49 | for (i = 2; str[i]; ++i) { 50 | char c = static_cast(::tolower(str[i])); 51 | if (c >= '0' && c <= '9') { 52 | out <<= 4; 53 | out += c - '0'; 54 | } else if (c >= 'a' && c <= 'f') { 55 | out <<= 4; 56 | out += c - 'a' + 10; 57 | } else { 58 | break; 59 | } 60 | } 61 | } else if ('\\' == str[0]) { // oct 62 | for (i = 0; str[i] >= '0' && str[i] < '8'; ++i) { 63 | out <<= 3; 64 | out += str[i] - '0'; 65 | } 66 | } else { // dec 67 | for (i = 0; str[i] >= '0' && str[i] <= '9'; ++i) { 68 | out *= 10; 69 | out += str[i] - '0'; 70 | } 71 | } 72 | 73 | if (NULL != left) { 74 | *left = str + i; 75 | } 76 | 77 | if (is_negative) { 78 | out = (~out) + 1; 79 | } 80 | 81 | return out; 82 | } 83 | } // namespace detail 84 | 85 | ini_value::ini_value() {} 86 | 87 | void ini_value::add(const std::string &val) { _data.push_back(val); } 88 | 89 | void ini_value::add(const char *begin, const char *end) { _data.push_back(std::string(begin, end)); } 90 | 91 | bool ini_value::empty() const { return _data.empty() && _chirldren_nodes.empty(); } 92 | 93 | bool ini_value::has_data() const { return false == _data.empty(); } 94 | 95 | size_t ini_value::size() const { return _data.size(); } 96 | 97 | void ini_value::clear() { 98 | _data.clear(); 99 | _chirldren_nodes.clear(); 100 | } 101 | 102 | ini_value &ini_value::operator[](const std::string key) { return _chirldren_nodes[key]; } 103 | 104 | ini_value::node_type &ini_value::get_children() { return _chirldren_nodes; } 105 | 106 | const ini_value::node_type &ini_value::get_children() const { return _chirldren_nodes; } 107 | 108 | const std::string &ini_value::get_empty_string() { 109 | static std::string empty_data; 110 | return empty_data; 111 | } 112 | 113 | const std::string &ini_value::as_cpp_string(size_t index) const { 114 | if (index < _data.size()) { 115 | return _data[index]; 116 | } 117 | 118 | return get_empty_string(); 119 | } 120 | 121 | char ini_value::as_char(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 122 | 123 | short ini_value::as_short(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 124 | 125 | int ini_value::as_int(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 126 | 127 | long ini_value::as_long(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 128 | 129 | long long ini_value::as_longlong(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 130 | 131 | double ini_value::as_double(size_t index) const { return as(index); } 132 | 133 | float ini_value::as_float(size_t index) const { return as(index); } 134 | 135 | const char *ini_value::as_string(size_t index) const { return as_cpp_string(index).c_str(); } 136 | 137 | // ============ unsigned ============ 138 | unsigned char ini_value::as_uchar(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 139 | 140 | unsigned short ini_value::as_ushort(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 141 | 142 | unsigned int ini_value::as_uint(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 143 | 144 | unsigned long ini_value::as_ulong(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 145 | 146 | unsigned long long ini_value::as_ulonglong(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 147 | 148 | int8_t ini_value::as_int8(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 149 | 150 | uint8_t ini_value::as_uint8(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 151 | 152 | int16_t ini_value::as_int16(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 153 | 154 | uint16_t ini_value::as_uint16(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 155 | 156 | int32_t ini_value::as_int32(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 157 | 158 | uint32_t ini_value::as_uint32(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 159 | 160 | int64_t ini_value::as_int64(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 161 | 162 | uint64_t ini_value::as_uint64(size_t index) const { return detail::str2int(as_cpp_string(index).c_str()); } 163 | 164 | duration_value ini_value::as_duration(size_t index) const { 165 | duration_value ret; 166 | clear_data(ret); 167 | 168 | if (index >= _data.size()) { 169 | return ret; 170 | } 171 | 172 | detail::const_cstring word_begin = NULL; 173 | time_t tm_val = detail::str2int(as_cpp_string(index).c_str(), &word_begin); 174 | while (word_begin && *word_begin) { 175 | if (analysis::spaces::test_char(*word_begin)) { 176 | ++word_begin; 177 | continue; 178 | } 179 | break; 180 | } 181 | 182 | detail::const_cstring word_end = word_begin; 183 | while (word_end && ((*word_end >= 'A' && *word_end <= 'Z') || (*word_end >= 'a' && *word_end <= 'z'))) { 184 | ++word_end; 185 | } 186 | 187 | std::string unit; 188 | if (word_begin && word_end && word_end > word_begin) { 189 | unit.assign(word_begin, word_end); 190 | std::transform(unit.begin(), unit.end(), unit.begin(), detail::tolower); 191 | } 192 | 193 | bool fallback = true; 194 | do { 195 | if (unit.empty() || unit == "s" || unit == "sec" || unit == "second" || unit == "seconds") { 196 | break; 197 | } 198 | 199 | if (unit == "ms" || unit == "millisecond" || unit == "milliseconds") { 200 | fallback = false; 201 | ret.sec = tm_val / 1000; 202 | ret.nsec = (tm_val % 1000) * 1000000; 203 | break; 204 | } 205 | 206 | if (unit == "us" || unit == "microsecond" || unit == "microseconds") { 207 | fallback = false; 208 | ret.sec = tm_val / 1000000; 209 | ret.nsec = (tm_val % 1000000) * 1000; 210 | break; 211 | } 212 | 213 | if (unit == "ns" || unit == "nanosecond" || unit == "nanoseconds") { 214 | fallback = false; 215 | ret.sec = tm_val / 1000000000; 216 | ret.nsec = tm_val % 1000000000; 217 | break; 218 | } 219 | 220 | if (unit == "m" || unit == "minute" || unit == "minutes") { 221 | fallback = false; 222 | ret.sec = tm_val * 60; 223 | break; 224 | } 225 | 226 | if (unit == "h" || unit == "hour" || unit == "hours") { 227 | fallback = false; 228 | ret.sec = tm_val * 3600; 229 | break; 230 | } 231 | 232 | if (unit == "d" || unit == "day" || unit == "days") { 233 | fallback = false; 234 | ret.sec = tm_val * 3600 * 24; 235 | break; 236 | } 237 | 238 | if (unit == "w" || unit == "week" || unit == "weeks") { 239 | fallback = false; 240 | ret.sec = tm_val * 3600 * 24 * 7; 241 | break; 242 | } 243 | 244 | } while (false); 245 | 246 | // fallback to second 247 | if (fallback) { 248 | ret.sec = tm_val; 249 | } 250 | 251 | return ret; 252 | } 253 | 254 | namespace analysis { 255 | // space 256 | bool spaces::test_char(char c) { return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); } 257 | 258 | bool spaces::test(const char *begin, const char *end) { return begin < end && test_char(*begin); } 259 | 260 | const char *spaces::parse(const char *begin, const char *end) { 261 | while (begin < end && test_char(*begin)) { 262 | ++begin; 263 | } 264 | 265 | return begin; 266 | } 267 | 268 | // comment 269 | bool comment::test(const char *begin, const char *end) { return begin < end && ((*begin) == '#' || (*begin) == ';'); } 270 | 271 | const char *comment::parse(const char *begin, const char *end) { 272 | if (false == test(begin, end)) { 273 | return begin; 274 | } 275 | 276 | return end; 277 | } 278 | 279 | // identify 280 | bool identify::test(const char *begin, const char *end) { return begin < end; } 281 | 282 | const char *identify::parse(const char *begin, const char *end) { 283 | _begin_ptr = _end_ptr = begin; 284 | if (false == test(begin, end)) { 285 | return begin; 286 | } 287 | 288 | while (begin < end && (*begin) != ':' && (*begin) != '.' && (*begin) != '=') { 289 | _end_ptr = (++begin); 290 | } 291 | 292 | // trim right 293 | while (_end_ptr > _begin_ptr && spaces::test_char(*(_end_ptr - 1))) 294 | --_end_ptr; 295 | 296 | return begin; 297 | } 298 | 299 | // key 300 | bool key::test(const char *begin, const char *end) { return begin < end; } 301 | 302 | const char *key::parse(const char *begin, const char *end) { 303 | while (begin < end) { 304 | if (false == test(begin, end)) { 305 | return begin; 306 | } 307 | 308 | identify idt; 309 | begin = idt.parse(begin, end); 310 | if (idt._begin_ptr >= idt._end_ptr) { 311 | return begin; 312 | } 313 | 314 | // 提取key 315 | _keys.push_back(std::make_pair(idt._begin_ptr, idt._end_ptr)); 316 | 317 | spaces spliter; 318 | begin = spliter.parse(begin, end); 319 | 320 | if (begin >= end || (*begin) != '.') { 321 | return begin; 322 | } 323 | 324 | begin = spliter.parse(begin + 1, end); 325 | } 326 | 327 | return begin; 328 | } 329 | 330 | // section 331 | bool section::test(const char *begin, const char *end) { return begin < end && (*begin) == '['; } 332 | 333 | const char *section::parse(const char *begin, const char *end) { 334 | if (false == test(begin, end)) { 335 | return begin; 336 | } 337 | 338 | ++begin; 339 | spaces spliter; 340 | bool push_front = true; 341 | while (begin < end) { 342 | // trim left 343 | begin = spliter.parse(begin, end); 344 | const char *start = begin; 345 | while (begin < end && (*begin) != ':' && (*begin) != '.' && (*begin) != ']') { 346 | ++begin; 347 | } 348 | 349 | char stop_char = begin < end ? (*begin) : 0; 350 | 351 | 352 | // trim right 353 | while (begin > start && spaces::test_char(*(begin - 1))) { 354 | --begin; 355 | } 356 | 357 | if (start < begin) { 358 | // 提取key 359 | if (push_front) { 360 | _keys.push_front(std::make_pair(start, begin)); 361 | } else { 362 | _keys.push_back(std::make_pair(start, begin)); 363 | } 364 | } 365 | 366 | 367 | begin = spliter.parse(begin, end); 368 | 369 | if (begin >= end) { 370 | break; 371 | } 372 | 373 | // 略过结尾的 ] 字符 374 | if ((*begin) == ']') { 375 | ++begin; 376 | break; 377 | } 378 | 379 | if ('.' == stop_char) { 380 | push_front = false; 381 | } else if (':' == stop_char) { 382 | push_front = true; 383 | } 384 | 385 | begin = spliter.parse(begin + 1, end); 386 | } 387 | 388 | return begin; 389 | } 390 | 391 | // string 392 | char string::_convert_map[1 << (sizeof(char) * 8)] = {0}; 393 | 394 | void string::init_conver_map() { 395 | if (_convert_map[(int)'0']) { 396 | return; 397 | } 398 | 399 | _convert_map[(int)'0'] = '\0'; 400 | _convert_map[(int)'a'] = '\a'; 401 | _convert_map[(int)'b'] = '\b'; 402 | _convert_map[(int)'f'] = '\f'; 403 | _convert_map[(int)'r'] = '\r'; 404 | _convert_map[(int)'n'] = '\n'; 405 | _convert_map[(int)'t'] = '\t'; 406 | _convert_map[(int)'v'] = '\v'; 407 | _convert_map[(int)'\\'] = '\\'; 408 | _convert_map[(int)'\''] = '\''; 409 | _convert_map[(int)'\"'] = '\"'; 410 | } 411 | 412 | bool string::test(const char *begin, const char *end) { return begin < end && ((*begin) == '\'' || (*begin) == '\"'); } 413 | 414 | const char *string::parse(const char *begin, const char *end, bool enable_convert) { 415 | if (false == test(begin, end)) { 416 | return begin; 417 | } 418 | 419 | init_conver_map(); 420 | char quot = *(begin++); 421 | 422 | // 禁止转义字符串 423 | if (false == enable_convert) { 424 | const char *start = begin; 425 | while (begin < end && (*begin) != quot) { 426 | ++begin; 427 | } 428 | _value.assign(start, begin); 429 | 430 | // 封闭字符串 431 | if (begin < end) { 432 | ++begin; 433 | } 434 | } else { // 允许转义的逻辑复杂一些 435 | while (begin < end && (*begin) != quot) { 436 | // 转义字符 437 | if ((*begin) == '\\' && begin + 1 < end) { 438 | ++begin; 439 | _value += _convert_map[(int)*(begin++)]; 440 | continue; 441 | } 442 | 443 | // 普通字符 444 | _value += *(begin++); 445 | } 446 | 447 | // 封闭字符串 448 | if (begin < end) { 449 | ++begin; 450 | } 451 | } 452 | 453 | return begin; 454 | } 455 | 456 | // value 457 | bool value::test(const char *begin, const char *end) { return begin < end; } 458 | 459 | const char *value::parse(const char *begin, const char *end) { 460 | if (false == test(begin, end)) { 461 | return begin; 462 | } 463 | 464 | // trim left 465 | spaces spliter; 466 | begin = spliter.parse(begin, end); 467 | 468 | string rule; 469 | comment com_s; 470 | while (begin < end) { 471 | 472 | if (rule.test(begin, end)) { 473 | rule._value.clear(); 474 | begin = rule.parse(begin, end, (*begin) == '\"'); 475 | _value += rule._value; 476 | continue; 477 | } 478 | 479 | if (com_s.test(begin, end)) { 480 | begin = com_s.parse(begin, end); 481 | continue; 482 | } 483 | 484 | _value += *(begin++); 485 | } 486 | 487 | // trim right 488 | size_t len = _value.size(); 489 | while (len > 0) { 490 | if (false == spaces::test_char(_value[len - 1])) { 491 | break; 492 | } 493 | 494 | --len; 495 | } 496 | 497 | _value = _value.substr(0, len); 498 | 499 | return begin; 500 | } 501 | 502 | // expression 503 | bool expression::test(const char *begin, const char *end) { return _key.test(begin, end); } 504 | 505 | const char *expression::parse(const char *begin, const char *end) { 506 | if (false == test(begin, end)) { 507 | return begin; 508 | } 509 | 510 | spaces spliter; 511 | 512 | begin = _key.parse(begin, end); 513 | begin = spliter.parse(begin, end); 514 | 515 | if (begin >= end || (*begin) != '=') { 516 | return begin; 517 | } 518 | 519 | begin = spliter.parse(begin + 1, end); 520 | 521 | return _value.parse(begin, end); 522 | } 523 | 524 | // sentence 525 | bool sentence::test(const char *begin, const char *end) { return begin < end; } 526 | 527 | const char *sentence::parse(const char *begin, const char *end) { 528 | _sect.first = false; 529 | _exp.first = false; 530 | 531 | if (false == test(begin, end)) { 532 | return begin; 533 | } 534 | 535 | spaces spliter; 536 | begin = spliter.parse(begin, end); 537 | 538 | // 空语句 539 | if (begin >= end) { 540 | return begin; 541 | } 542 | 543 | // 纯注释语句 544 | comment com_s; 545 | if (com_s.test(begin, end)) { 546 | return com_s.parse(begin, end); 547 | } 548 | 549 | // section语句 550 | _sect.first = _sect.second.test(begin, end); 551 | if (_sect.first) { 552 | return _sect.second.parse(begin, end); 553 | } 554 | 555 | // expression语句 556 | _exp.first = _exp.second.test(begin, end); 557 | if (_exp.first) { 558 | return _exp.second.parse(begin, end); 559 | } 560 | 561 | return begin; 562 | } 563 | } // namespace analysis 564 | 565 | ini_loader::ini_loader() { _current_node_ptr = &_root_node; } 566 | 567 | ini_loader::~ini_loader() {} 568 | 569 | int ini_loader::load_stream(std::istream &in, bool is_append) { 570 | 571 | if (false == is_append) { 572 | clear(); 573 | } 574 | 575 | std::string test_bom; 576 | test_bom.resize(3, 0); 577 | test_bom[0] = static_cast(in.get()); 578 | test_bom[1] = static_cast(in.get()); 579 | test_bom[2] = static_cast(in.get()); 580 | const unsigned char utf8_bom[3] = {0xef, 0xbb, 0xbf}; 581 | 582 | if (0 == memcmp(test_bom.c_str(), utf8_bom, 3)) { 583 | test_bom.clear(); 584 | } 585 | 586 | std::string line; 587 | while (std::getline(in, line)) { 588 | if (!test_bom.empty()) { 589 | line = test_bom + line; 590 | test_bom.clear(); 591 | } 592 | analysis::sentence one_sentence; 593 | one_sentence.parse(line.c_str(), line.c_str() + line.size()); 594 | 595 | // section 节点会改变当前配置区域 596 | if (one_sentence._sect.first) { 597 | _current_node_ptr = &get_root_node(); 598 | analysis::section::list_type::iterator iter = one_sentence._sect.second._keys.begin(); 599 | for (; iter != one_sentence._sect.second._keys.end(); ++iter) { 600 | if (iter->first >= iter->second) { 601 | continue; 602 | } 603 | 604 | std::string key; 605 | key.assign(iter->first, iter->second); 606 | _current_node_ptr = &get_node(key, _current_node_ptr); 607 | } 608 | } 609 | 610 | // expression 节点为配置值 611 | if (one_sentence._exp.first) { 612 | ini_value *opr_node = &get_section(); 613 | analysis::key::list_type::iterator iter = one_sentence._exp.second._key._keys.begin(); 614 | for (; iter != one_sentence._exp.second._key._keys.end(); ++iter) { 615 | if (iter->first >= iter->second) { 616 | continue; 617 | } 618 | 619 | std::string key; 620 | key.assign(iter->first, iter->second); 621 | opr_node = &get_node(key, opr_node); 622 | } 623 | 624 | if (!one_sentence._exp.second._value._value.empty()) { 625 | opr_node->add(one_sentence._exp.second._value._value); 626 | } 627 | } 628 | } 629 | 630 | return EIEC_SUCCESS; 631 | } 632 | 633 | int ini_loader::load_file(const char *file_path, bool is_append) { 634 | if (NULL == file_path) { 635 | return EIEC_OPENFILE; 636 | } 637 | 638 | std::ifstream file_to_load; 639 | file_to_load.open(file_path, std::ios::in); 640 | if (false == file_to_load.is_open()) { 641 | return EIEC_OPENFILE; 642 | } 643 | 644 | if (false == is_append) { 645 | clear(); 646 | } 647 | 648 | return load_stream(file_to_load, is_append); 649 | } 650 | 651 | int ini_loader::load_file(const std::string &file_path, bool is_append) { return load_file(file_path.c_str(), is_append); } 652 | 653 | void ini_loader::clear() { 654 | _current_node_ptr = &_root_node; 655 | _root_node.clear(); 656 | } 657 | 658 | void ini_loader::set_section(const std::string &path) { _current_node_ptr = &get_node(path, &get_root_node()); } 659 | 660 | ini_value &ini_loader::get_section() { return *_current_node_ptr; } 661 | 662 | const ini_value &ini_loader::get_section() const { return *_current_node_ptr; } 663 | 664 | 665 | ini_value &ini_loader::get_root_node() { return _root_node; } 666 | 667 | const ini_value &ini_loader::get_root_node() const { return _root_node; } 668 | 669 | ini_value &ini_loader::get_node(const std::string &path, ini_value *father_ptr) { 670 | if (NULL == father_ptr) { 671 | father_ptr = &_root_node; 672 | } 673 | 674 | analysis::key _keys; 675 | const char *begin = path.c_str(); 676 | const char *end = begin + path.size(); 677 | 678 | analysis::spaces spliter; 679 | begin = spliter.parse(begin, end); 680 | 681 | _keys.parse(begin, end); 682 | analysis::section::list_type::iterator iter = _keys._keys.begin(); 683 | for (; iter != _keys._keys.end(); ++iter) { 684 | if (iter->first >= iter->second) { 685 | continue; 686 | } 687 | 688 | std::string key; 689 | key.assign(iter->first, iter->second); 690 | father_ptr = &get_child_node(key, father_ptr); 691 | } 692 | 693 | return *father_ptr; 694 | } 695 | 696 | ini_value &ini_loader::get_child_node(const std::string &path, ini_value *father_ptr) { 697 | if (NULL == father_ptr) { 698 | father_ptr = &_root_node; 699 | } 700 | 701 | return (*father_ptr)[path]; 702 | } 703 | 704 | void ini_loader::dump_to(const std::string &path, bool &val, bool is_force, size_t index) { 705 | ini_value &cur_node = get_node(path); 706 | 707 | if (false == cur_node.has_data() && false == is_force) { 708 | return; 709 | } 710 | 711 | // no data 712 | if (false == cur_node.has_data() && is_force) { 713 | val = false; 714 | return; 715 | } 716 | 717 | std::string trans = cur_node.as_cpp_string(index); 718 | std::transform(trans.begin(), trans.end(), trans.begin(), detail::tolower); 719 | val = true; 720 | 721 | if ("0" == trans || "false" == trans || "no" == trans || "disable" == trans || "disabled" == trans || "" == trans) { 722 | val = false; 723 | } 724 | } 725 | 726 | void ini_loader::dump_to(const std::string &path, std::string &val, bool is_force, size_t index) { 727 | ini_value &cur_node = get_node(path); 728 | if (cur_node.has_data() || is_force) { 729 | val = cur_node.as_cpp_string(index); 730 | } 731 | } 732 | 733 | void ini_loader::dump_to(const std::string &path, char *begin, char *end, bool is_force, size_t index) { 734 | ini_value &cur_node = get_node(path); 735 | if (cur_node.has_data() || is_force) { 736 | const std::string &val = cur_node.as_cpp_string(index); 737 | memcpy(begin, val.c_str(), std::min(end - begin, val.size())); 738 | if (static_cast(end - begin) > val.size()) { 739 | memset(begin + val.size(), 0, end - begin - val.size()); 740 | } 741 | } 742 | } 743 | } // namespace config 744 | } // namespace util 745 | -------------------------------------------------------------------------------- /ini_loader.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ini_loader.h 3 | * @brief ini解析器 4 | * Licensed under the MIT licenses. 5 | * 6 | * @note 与标准ini有略微不同,请注意 7 | * 1. 支持Section 8 | * 2. Secion支持父子关系,即 [ A : B : C ] 语法 9 | * 3. 支持多重父子关系,如 C.B.A = d 10 | * 4. 支持字符串转义,其中以'包裹的不进行转义,以"包裹的可进行转义,如 C.B.A = "Hello \r\n World!\0" 11 | * 5. 配置的Key名称不能包含引号('和")、点(.)、冒号(:)和方括号([]) 12 | * 6. 配置的Key名称区分大小写 13 | * 7. #符号也将是做行注释,与 ; 等价 14 | * 15 | * @version 1.0.1.0 16 | * @author owentou, owt5008137@live.com 17 | * @date 2013年11月16日 18 | * 19 | * @history 20 | * 2014-07-14: 修正空值问题, 优化API 21 | * 2015-02-02: 修正字符串未配置会导致崩溃的BUG 22 | * 2016-04-14: Section部分也支持使用.来分割层级 23 | */ 24 | 25 | #ifndef UTIL_CONFIG_INI_INILOADER_H 26 | #define UTIL_CONFIG_INI_INILOADER_H 27 | 28 | #pragma once 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | // ================= 版本号 ================= 40 | #define LIBINILOADER_VERSION "1.0.1.0" 41 | 42 | namespace util { 43 | namespace config { 44 | // ================= 错误码 ================= 45 | enum EN_INILOADER_ERROR_CODE { 46 | EIEC_SUCCESS = 0, 47 | EIEC_OPENFILE = -1, 48 | }; 49 | // 时间区间 50 | struct duration_value { 51 | time_t sec; // 秒 52 | time_t nsec; // 纳秒 53 | }; 54 | // ----------------- 错误码 ----------------- 55 | 56 | // ================= 存储层 ================= 57 | class ini_value { 58 | public: 59 | typedef std::map node_type; 60 | 61 | private: 62 | std::vector _data; 63 | node_type _chirldren_nodes; 64 | 65 | 66 | template 67 | inline static void clear_data(_Tt &) {} 68 | 69 | inline static void clear_data(float &data) { data = 0.0f; } 70 | inline static void clear_data(double &data) { data = 0.0; } 71 | inline static void clear_data(char *&data) { data = NULL; } 72 | inline static void clear_data(std::string &data) { data.clear(); } 73 | inline static void clear_data(bool &data) { data = false; } 74 | inline static void clear_data(char &data) { data = 0; } 75 | inline static void clear_data(short &data) { data = 0; } 76 | inline static void clear_data(int &data) { data = 0; } 77 | inline static void clear_data(long &data) { data = 0; } 78 | inline static void clear_data(long long &data) { data = 0; } 79 | inline static void clear_data(unsigned char &data) { data = 0; } 80 | inline static void clear_data(unsigned short &data) { data = 0; } 81 | inline static void clear_data(unsigned int &data) { data = 0; } 82 | inline static void clear_data(unsigned long &data) { data = 0; } 83 | inline static void clear_data(unsigned long long &data) { data = 0; } 84 | inline static void clear_data(duration_value &data) { 85 | data.sec = 0; 86 | data.nsec = 0; 87 | } 88 | 89 | public: 90 | ini_value(); 91 | 92 | void add(const std::string &val); 93 | void add(const char *begin, const char *end); 94 | 95 | // 节点操作 96 | bool empty() const; // like stl 97 | bool has_data() const; // like stl 98 | size_t size() const; // like stl 99 | void clear(); // like stl 100 | ini_value &operator[](const std::string key); 101 | node_type &get_children(); 102 | const node_type &get_children() const; 103 | 104 | static const std::string &get_empty_string(); 105 | 106 | 107 | private: 108 | template 109 | struct as_helper; 110 | 111 | template 112 | struct as_helper< ::util::config::duration_value, _TVOID> { 113 | inline static ::util::config::duration_value convert(const ini_value &val, size_t index) { return val.as_duration(index); } 114 | }; 115 | 116 | template 117 | struct as_helper { 118 | inline static int8_t convert(const ini_value &val, size_t index) { return val.as_int8(index); } 119 | }; 120 | 121 | template 122 | struct as_helper { 123 | inline static uint8_t convert(const ini_value &val, size_t index) { return val.as_uint8(index); } 124 | }; 125 | 126 | template 127 | struct as_helper { 128 | inline static int16_t convert(const ini_value &val, size_t index) { return val.as_int16(index); } 129 | }; 130 | 131 | template 132 | struct as_helper { 133 | inline static uint16_t convert(const ini_value &val, size_t index) { return val.as_uint16(index); } 134 | }; 135 | 136 | template 137 | struct as_helper { 138 | inline static int32_t convert(const ini_value &val, size_t index) { return val.as_int32(index); } 139 | }; 140 | 141 | template 142 | struct as_helper { 143 | inline static uint32_t convert(const ini_value &val, size_t index) { return val.as_uint32(index); } 144 | }; 145 | 146 | template 147 | struct as_helper { 148 | inline static int64_t convert(const ini_value &val, size_t index) { return val.as_int64(index); } 149 | }; 150 | 151 | template 152 | struct as_helper { 153 | inline static uint64_t convert(const ini_value &val, size_t index) { return val.as_uint64(index); } 154 | }; 155 | 156 | template 157 | struct as_helper { 158 | inline static const char *convert(const ini_value &val, size_t index) { return val.as_string(index); } 159 | }; 160 | 161 | template 162 | struct as_helper { 163 | inline static std::string convert(const ini_value &val, size_t index) { return val.as_cpp_string(index); } 164 | }; 165 | 166 | template 167 | struct as_helper { 168 | inline static _Tt string2any(const std::string &data) { 169 | _Tt ret; 170 | ini_value::clear_data(ret); 171 | if (!data.empty()) { 172 | std::stringstream s_stream; 173 | s_stream.str(data); 174 | s_stream >> ret; 175 | } 176 | return ret; 177 | } 178 | 179 | inline static _Tt convert(const ini_value &val, size_t index) { return string2any(val.as_cpp_string(index)); } 180 | }; 181 | 182 | public: 183 | // 数值转换操作 184 | template 185 | inline _Tt as(size_t index = 0) const { 186 | return as_helper<_Tt, void>::convert(*this, index); 187 | } 188 | 189 | // 获取存储对象的字符串 190 | const std::string &as_cpp_string(size_t index = 0) const; 191 | 192 | char as_char(size_t index = 0) const; 193 | 194 | short as_short(size_t index = 0) const; 195 | 196 | int as_int(size_t index = 0) const; 197 | 198 | long as_long(size_t index = 0) const; 199 | 200 | long long as_longlong(size_t index = 0) const; 201 | 202 | double as_double(size_t index = 0) const; 203 | 204 | float as_float(size_t index = 0) const; 205 | 206 | const char *as_string(size_t index = 0) const; 207 | 208 | unsigned char as_uchar(size_t index = 0) const; 209 | 210 | unsigned short as_ushort(size_t index = 0) const; 211 | 212 | unsigned int as_uint(size_t index = 0) const; 213 | 214 | unsigned long as_ulong(size_t index = 0) const; 215 | 216 | unsigned long long as_ulonglong(size_t index = 0) const; 217 | 218 | int8_t as_int8(size_t index = 0) const; 219 | 220 | uint8_t as_uint8(size_t index = 0) const; 221 | 222 | int16_t as_int16(size_t index = 0) const; 223 | 224 | uint16_t as_uint16(size_t index = 0) const; 225 | 226 | int32_t as_int32(size_t index = 0) const; 227 | 228 | uint32_t as_uint32(size_t index = 0) const; 229 | 230 | int64_t as_int64(size_t index = 0) const; 231 | 232 | uint64_t as_uint64(size_t index = 0) const; 233 | 234 | duration_value as_duration(size_t index = 0) const; 235 | }; 236 | // ----------------- 存储层 ----------------- 237 | 238 | // ================= 词法状态机 ================= 239 | namespace analysis { 240 | // space 241 | struct spaces { 242 | static bool test_char(char c); 243 | bool test(const char *begin, const char *end); 244 | const char *parse(const char *begin, const char *end); 245 | }; 246 | 247 | // comment 248 | struct comment { 249 | bool test(const char *begin, const char *end); 250 | const char *parse(const char *begin, const char *end); 251 | }; 252 | 253 | // identify 254 | struct identify { 255 | const char *_begin_ptr; 256 | const char *_end_ptr; 257 | 258 | bool test(const char *begin, const char *end); 259 | const char *parse(const char *begin, const char *end); 260 | }; 261 | 262 | // key 263 | struct key { 264 | typedef std::list > list_type; 265 | list_type _keys; 266 | 267 | bool test(const char *begin, const char *end); 268 | const char *parse(const char *begin, const char *end); 269 | }; 270 | 271 | // section 272 | struct section { 273 | typedef std::list > list_type; 274 | list_type _keys; 275 | 276 | bool test(const char *begin, const char *end); 277 | const char *parse(const char *begin, const char *end); 278 | }; 279 | 280 | // string 281 | struct string { 282 | static char _convert_map[1 << (sizeof(char) * 8)]; 283 | void init_conver_map(); 284 | 285 | std::string _value; 286 | 287 | bool test(const char *begin, const char *end); 288 | const char *parse(const char *begin, const char *end, bool enable_convert = false); 289 | }; 290 | 291 | // value 292 | struct value { 293 | std::string _value; 294 | 295 | bool test(const char *begin, const char *end); 296 | const char *parse(const char *begin, const char *end); 297 | }; 298 | 299 | // expression 300 | struct expression { 301 | key _key; 302 | value _value; 303 | 304 | bool test(const char *begin, const char *end); 305 | const char *parse(const char *begin, const char *end); 306 | }; 307 | 308 | // sentence 309 | struct sentence { 310 | std::pair _sect; 311 | std::pair _exp; 312 | 313 | bool test(const char *begin, const char *end); 314 | const char *parse(const char *begin, const char *end); 315 | }; 316 | } // namespace analysis 317 | // ----------------- 词法状态机 ----------------- 318 | 319 | class ini_loader { 320 | private: 321 | ini_value _root_node; // root node 322 | ini_value *_current_node_ptr; 323 | 324 | public: 325 | ini_loader(); 326 | ~ini_loader(); 327 | 328 | /** 329 | * @brief 从流读取数据 330 | * @param in 输入流 331 | * @param is_append 是否是追加模式 332 | * @return 返回码 333 | * @see EN_INILOADER_ERROR_CODE 334 | */ 335 | int load_stream(std::istream &in, bool is_append = false); 336 | 337 | /** 338 | * @brief 从文件取数据 339 | * @param file_path 输入文件 340 | * @param is_append 是否是追加模式 341 | * @return 返回码 342 | * @see EN_INILOADER_ERROR_CODE 343 | */ 344 | int load_file(const char *file_path, bool is_append = false); 345 | 346 | /** 347 | * @brief 从文件取数据 348 | * @param file_path 输入文件 349 | * @param is_append 是否是追加模式 350 | * @return 返回码 351 | * @see EN_INILOADER_ERROR_CODE 352 | */ 353 | int load_file(const std::string &file_path, bool is_append = false); 354 | 355 | /** 356 | * @brief 清空 357 | */ 358 | void clear(); 359 | 360 | /** 361 | * @brief 设置当前配置结构根节点路径 362 | * @param path 路径 363 | */ 364 | void set_section(const std::string &path); 365 | 366 | /** 367 | * @brief 获取当前配置结构根节点 368 | * @return 当前配置结构根节点 369 | */ 370 | ini_value &get_section(); 371 | 372 | /** 373 | * @brief 获取当前配置结构根节点 374 | * @return 当前配置结构根节点 375 | */ 376 | const ini_value &get_section() const; 377 | 378 | /** 379 | * @brief 获取根节点 380 | * @return 根节点 381 | */ 382 | ini_value &get_root_node(); 383 | 384 | /** 385 | * @brief 获取根节点 386 | * @return 根节点 387 | */ 388 | const ini_value &get_root_node() const; 389 | 390 | /** 391 | * @brief 根据目录获取子节点 392 | * @param path 节点相对路径 393 | * @param father_ptr 父节点,设为空则相对于根节点 394 | * @return 子节点 395 | * @note 如果子节点不存在会创建空列表节点 396 | */ 397 | ini_value &get_node(const std::string &path, ini_value *father_ptr = NULL); 398 | 399 | /** 400 | * @brief 根据子节点名称获取子节点 401 | * @param path 子节点名称(注意不是路径) 402 | * @param father_ptr 父节点,设为空则相对于根节点 403 | * @return 子节点 404 | * @note 如果子节点不存在会创建空列表节点 405 | */ 406 | ini_value &get_child_node(const std::string &path, ini_value *father_ptr = NULL); 407 | 408 | // ========================= 单值容器转储 ========================= 409 | 410 | /** 411 | * @brief 配置转储 412 | * @param path 配置路径 413 | * @param val 转储目标 414 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 415 | * @param index 转储索引,默认是第一个值 416 | */ 417 | template 418 | void dump_to(const std::string &path, Ty &val, bool is_force = false, size_t index = 0) { 419 | ini_value &cur_node = get_node(path); 420 | if (cur_node.has_data() || is_force) { 421 | val = cur_node.as(index); 422 | } 423 | } 424 | 425 | /** 426 | * @brief 配置转储 427 | * @param path 配置路径 428 | * @param val 转储目标 429 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 430 | * @param index 转储索引,默认是第一个值 431 | */ 432 | void dump_to(const std::string &path, bool &val, bool is_force = false, size_t index = 0); 433 | 434 | /** 435 | * @brief 配置转储 436 | * @param path 配置路径 437 | * @param val 转储目标 438 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 439 | * @param index 转储索引,默认是第一个值 440 | */ 441 | void dump_to(const std::string &path, std::string &val, bool is_force = false, size_t index = 0); 442 | 443 | /** 444 | * @brief 配置转储 - 字符串 445 | * @param path 配置路径 446 | * @param begin 转储目标起始地址 447 | * @param end 转储目标边界地址 448 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 449 | * @param index 转储索引,默认是第一个值 450 | */ 451 | void dump_to(const std::string &path, char *begin, char *end, bool is_force = false, size_t index = 0); 452 | 453 | /** 454 | * @brief 配置转储 - 字符串 455 | * @param path 配置路径 456 | * @param val 转储目标 457 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 458 | * @param index 转储索引,默认是第一个值 459 | */ 460 | template 461 | void dump_to(const std::string &path, char (&val)[MAX_COUNT], bool is_force = false, size_t index = 0) { 462 | dump_to(path, val, val + MAX_COUNT, is_force, index); 463 | } 464 | 465 | // ========================= 多值容器转储 ========================= 466 | /** 467 | * @brief 配置转储 - 容器 468 | * @param path 配置路径 469 | * @param val 转储目标 470 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 471 | * @note 容器足够大时,会尝试转储所有配置 472 | */ 473 | template 474 | void dump_to(const std::string &path, std::vector &val, bool is_force = false) { 475 | if (is_force) { 476 | val.clear(); 477 | } 478 | 479 | ini_value &cur_node = get_node(path); 480 | for (size_t i = 0; i < cur_node.size(); ++i) { 481 | val.push_back(Ty()); 482 | Ty &new_node = val.back(); 483 | dump_to(path, new_node, is_force, i); 484 | } 485 | } 486 | 487 | /** 488 | * @brief 配置转储 - 容器 489 | * @param path 配置路径 490 | * @param val 转储目标 491 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 492 | * @note 容器足够大时,会尝试转储所有配置 493 | */ 494 | template 495 | void dump_to(const std::string &path, std::list &val, bool is_force = false) { 496 | if (is_force) { 497 | val.clear(); 498 | } 499 | 500 | ini_value &cur_node = get_node(path); 501 | for (size_t i = 0; i < cur_node.size(); ++i) { 502 | val.push_back(Ty()); 503 | Ty &new_node = val.back(); 504 | dump_to(path, new_node, is_force, i); 505 | } 506 | } 507 | 508 | /** 509 | * @brief 配置转储 - 数组 510 | * @param path 配置路径 511 | * @param val 转储目标 512 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 513 | * @note 容器足够大时,会尝试转储所有配置 514 | */ 515 | template 516 | void dump_to(const std::string &path, Ty (&val)[MAX_COUNT], bool is_force = false) { 517 | ini_value &cur_node = get_node(path); 518 | for (size_t i = 0; i < cur_node.size() && i < MAX_COUNT; ++i) { 519 | dump_to(path, val[i], is_force, i); 520 | } 521 | } 522 | 523 | /** 524 | * @brief 配置转储 - 迭代器 525 | * @param path 配置路径 526 | * @param begin 转储目标起始迭代器 527 | * @param end 转储目标边界迭代器 528 | * @param is_force 是否是强制转储,强制在找不到路径对应的配置项时会尝试清空数据 529 | * @note 容器足够大时,会尝试转储所有配置 530 | */ 531 | template 532 | void dump_to(const std::string &path, TIter begin, TIter end, bool is_force = false) { 533 | size_t index = 0; 534 | ini_value &cur_node = get_node(path); 535 | for (TIter i = begin; i != end && index < cur_node.size(); ++index, ++i) { 536 | dump_to(path, *i, is_force, index); 537 | } 538 | } 539 | }; 540 | } // namespace config 541 | } // namespace util 542 | 543 | #endif 544 | -------------------------------------------------------------------------------- /sample/config.ini: -------------------------------------------------------------------------------- 1 | [system] 2 | log.level = 6 ; DEBUG日志是6 3 | log.auto_update_time = true ; 自动更新时间 4 | log.print_file = true ; 打印文件位置 5 | log.print_function = true ; 打印调用函数名 6 | log.print_type = true ; 打印日志类型 7 | log.print_time = "[%Y-%m-%d %H:%M:%S]" ; 打印日志时间(@see http://en.cppreference.com/w/cpp/chrono/c/strftime) 8 | 9 | log.std.level.min = 1 10 | log.std.level.max = 3 11 | 12 | log.fs.level.min = 1 13 | log.fs.level.max = 6 14 | log.fs.path = /log/client.%Y-%m-%d 15 | log.fs.suffix = .%d.log 16 | log.fs.file_number = 10 17 | log.fs.file_size = 262144 ; 256KB 18 | log.fs.enable_buffer = false ; 允许日志文件缓写 19 | 20 | 21 | interval_ns = 123 ns 22 | interval_us = 123 us 23 | interval_ms = 123ms 24 | interval_s = 123 25 | interval_m = 123m 26 | interval_h = 123hours 27 | interval_d = 123days 28 | interval_w = 123weeks 29 | 30 | [resource] 31 | res.dir = "music" ; 附加资源搜索路径列表 32 | res.dir = "images" 33 | res.dir = "res" 34 | 35 | [resource . script . client] 36 | dir = "common"; 37 | dir = "src" 38 | 39 | cdir = "" ; 40 | 41 | [resource.script.vserver] 42 | dir = "common"; 43 | dir = "vserver" 44 | cdir = "" ; 45 | main = "script/vserver/main.lua" 46 | logic_frame_duration = 200 ; 47 | logic_x = 30 ; 48 | logic_x_offset = 28 ; 49 | logic_block_init_number = 128 ; 50 | 51 | [channel] 52 | mem_bus.buffer.max_length = 2097152 ; 2MB, 每个内存通道的缓冲区大小 53 | network.buffer.max_length = 131072 ; 128KB, 网络通道默认缓冲区大小 54 | vserver.buffer.max_length = 131072 ; 128KB, 虚拟服务器接收缓冲区大小 55 | 56 | -------------------------------------------------------------------------------- /sample/sample1.cpp: -------------------------------------------------------------------------------- 1 | #include "../ini_loader.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #define CONFIG_FILE "config.ini" 8 | 9 | struct GameConf { 10 | GameConf(); 11 | struct ChannelConfig { 12 | size_t membus_buffer_length; 13 | size_t network_buffer_length; 14 | size_t vserver_buffer_length; 15 | }; 16 | ChannelConfig channel; 17 | 18 | struct LogConfig { 19 | int level; 20 | bool auto_update_time; 21 | bool print_file; 22 | bool print_function; 23 | bool print_type; 24 | std::string print_time; 25 | 26 | int std_level_min; 27 | int std_level_max; 28 | 29 | int fs_level_min; 30 | int fs_level_max; 31 | std::string fs_path; 32 | std::string fs_suffix; 33 | size_t fs_file_number; 34 | size_t fs_file_size; 35 | bool fs_enable_buffer; 36 | }; 37 | LogConfig log; 38 | 39 | struct VServerConfig { 40 | std::string main; 41 | uint32_t logic_frame_duration; 42 | int32_t logic_x; 43 | int32_t logic_x_offset; 44 | size_t logic_block_init_number; 45 | }; 46 | VServerConfig vserver; 47 | 48 | bool init(); 49 | }; 50 | 51 | GameConf::GameConf() {} 52 | 53 | bool GameConf::init() { 54 | util::config::ini_loader conf_loader; 55 | 56 | conf_loader.load_file(CONFIG_FILE); 57 | 58 | // 通道配置 59 | { 60 | conf_loader.dump_to("channel.mem_bus.buffer.max_length", channel.membus_buffer_length); 61 | conf_loader.dump_to("channel.network.buffer.max_length", channel.network_buffer_length); 62 | conf_loader.dump_to("channel.vserver.buffer.max_length", channel.vserver_buffer_length); 63 | } 64 | 65 | 66 | // 默认资源和脚本目录配置 67 | { 68 | std::list paths; 69 | conf_loader.dump_to("resource.res.dir", paths); 70 | 71 | paths.clear(); 72 | conf_loader.dump_to("resource.script.client.dir", paths); 73 | 74 | paths.clear(); 75 | conf_loader.dump_to("resource.script.client.cdir", paths); 76 | 77 | paths.clear(); 78 | conf_loader.dump_to("resource.script.vserver.dir", paths); 79 | 80 | paths.clear(); 81 | conf_loader.dump_to("resource.script.vserver.cdir", paths); 82 | 83 | conf_loader.dump_to("resource.script.vserver.main", vserver.main); 84 | conf_loader.dump_to("resource.script.vserver.logic_frame_duration", vserver.logic_frame_duration); 85 | conf_loader.dump_to("resource.script.vserver.logic_x", vserver.logic_x); 86 | conf_loader.dump_to("resource.script.vserver.logic_x_offset", vserver.logic_x_offset); 87 | conf_loader.dump_to("resource.script.vserver.logic_block_init_number", vserver.logic_block_init_number); 88 | } 89 | 90 | // 日志配置 91 | { 92 | conf_loader.dump_to("system.log.level", log.level); 93 | conf_loader.dump_to("system.log.auto_update_time", log.auto_update_time); 94 | conf_loader.dump_to("system.log.print_file", log.print_file); 95 | conf_loader.dump_to("system.log.print_function", log.print_function); 96 | conf_loader.dump_to("system.log.print_type", log.print_type); 97 | conf_loader.dump_to("system.log.print_time", log.print_time); 98 | 99 | conf_loader.dump_to("system.log.std.level.min", log.std_level_min); 100 | conf_loader.dump_to("system.log.std.level.max", log.std_level_max); 101 | 102 | conf_loader.dump_to("system.log.fs.level.min", log.fs_level_min); 103 | conf_loader.dump_to("system.log.fs.level.max", log.fs_level_max); 104 | conf_loader.dump_to("system.log.fs.path", log.fs_path); 105 | conf_loader.dump_to("system.log.fs.suffix", log.fs_suffix); 106 | conf_loader.dump_to("system.log.fs.file_number", log.fs_file_number); 107 | conf_loader.dump_to("system.log.fs.file_size", log.fs_file_size); 108 | conf_loader.dump_to("system.log.fs.enable_buffer", log.fs_enable_buffer); 109 | } 110 | 111 | // 转储时间周期 112 | { 113 | util::config::duration_value dur; 114 | conf_loader.dump_to("system.interval_ns", dur, true); 115 | printf("system.interval_ns: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 116 | conf_loader.dump_to("system.interval_us", dur, true); 117 | printf("system.interval_us: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 118 | conf_loader.dump_to("system.interval_ms", dur, true); 119 | printf("system.interval_ms: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 120 | conf_loader.dump_to("system.interval_s", dur, true); 121 | printf("system.interval_s: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 122 | conf_loader.dump_to("system.interval_m", dur, true); 123 | printf("system.interval_m: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 124 | conf_loader.dump_to("system.interval_h", dur, true); 125 | printf("system.interval_h: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 126 | conf_loader.dump_to("system.interval_d", dur, true); 127 | printf("system.interval_d: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 128 | conf_loader.dump_to("system.interval_w", dur, true); 129 | printf("system.interval_w: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 130 | } 131 | 132 | return true; 133 | } 134 | 135 | 136 | int main() { 137 | GameConf gconf; 138 | 139 | puts("===== before load ini ====="); 140 | printf("system.log.print_time: %s(%p)\n", gconf.log.print_time.c_str(), gconf.log.print_time.c_str()); 141 | printf("resource.script.vserver.main: %s\n", gconf.vserver.main.c_str()); 142 | 143 | gconf.init(); 144 | 145 | puts("===== after load ini ====="); 146 | printf("system.log.print_time: %s(%p)\n", gconf.log.print_time.c_str(), gconf.log.print_time.c_str()); 147 | printf("resource.script.vserver.main: %s\n", gconf.vserver.main.c_str()); 148 | return 0; 149 | } -------------------------------------------------------------------------------- /sample/sample2.cpp: -------------------------------------------------------------------------------- 1 | #include "ini_loader.h" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | #define CONFIG_FILE "test.ini" 8 | 9 | int main() { 10 | util::config::ini_loader cfg_loader; 11 | 12 | cfg_loader.load_file(CONFIG_FILE); 13 | 14 | 15 | // 转储整数 16 | { 17 | int t1 = 9999, t2 = 9999; 18 | cfg_loader.dump_to("a.b.c1", t1); 19 | cfg_loader.dump_to("a.b.c4", t2, true); 20 | printf("a.b.c1 = %d\na.b.c4 = %d\n", t1, t2); 21 | } 22 | 23 | // 转储浮点数 24 | { 25 | float t1 = 0.0; 26 | cfg_loader.dump_to("a.b.c2", t1); 27 | printf("a.b.c2 = %f\n", t1); 28 | } 29 | 30 | // 转储字符串 31 | { 32 | char t1[32] = {0}; 33 | std::string t2, t3 = "0000000000000000"; 34 | std::string t4, t5; 35 | 36 | cfg_loader.dump_to("d.c.b.a.e.f1", t2); // 字符串 37 | cfg_loader.dump_to("d.c.b.a.e.f1", t3.begin(), t3.end()); // 字符串迭代器 38 | cfg_loader.dump_to("d.c.b.a.e.f2", t1); // 字符串 39 | cfg_loader.dump_to("d.c.b.a.e.f2", t1 + 16, t1 + 32); // 字符串指针迭代器 40 | cfg_loader.dump_to("a.b.c3", t4); // 带不可打印字符的字符串 41 | cfg_loader.dump_to("d.c.b.a.e.f3", t5); // 字符串 - Section使用.分割层级 42 | 43 | printf("len(t2) = %d\nlen(t3) = %d\n", (int)t2.size(), (int)t3.size()); 44 | printf("d.c.b.a.e.f2 = %s\n", t1); 45 | printf("d.c.b.a.e.f2 = %s(+16)\n", t1 + 16); 46 | printf("d.c.b.a.e.f3 = %s\n", t5.c_str()); 47 | printf("a.b.c3 = %s\n", t4.c_str()); 48 | } 49 | 50 | // 转储到 vector 51 | { 52 | std::vector t1; 53 | std::list t2; 54 | cfg_loader.dump_to("a.arr", t1); 55 | cfg_loader.dump_to("a.bool", t2); 56 | 57 | for (size_t i = 0; i < t1.size(); ++i) { 58 | printf("t1[%d] = %s\n", (int)i, t1[i].c_str()); 59 | } 60 | 61 | size_t index = 0; 62 | for (std::list::iterator iter = t2.begin(); iter != t2.end(); ++iter) { 63 | printf("t2[%d] = %s\n", (int)index++, (*iter) ? "true" : "false"); 64 | } 65 | } 66 | 67 | // 转储时间周期 68 | { 69 | util::config::duration_value dur; 70 | cfg_loader.dump_to("system.interval_ns", dur, true); 71 | printf("system.interval_ns: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 72 | cfg_loader.dump_to("system.interval_us", dur, true); 73 | printf("system.interval_us: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 74 | cfg_loader.dump_to("system.interval_ms", dur, true); 75 | printf("system.interval_ms: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 76 | cfg_loader.dump_to("system.interval_s", dur, true); 77 | printf("system.interval_s: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 78 | cfg_loader.dump_to("system.interval_m", dur, true); 79 | printf("system.interval_m: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 80 | cfg_loader.dump_to("system.interval_h", dur, true); 81 | printf("system.interval_h: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 82 | cfg_loader.dump_to("system.interval_d", dur, true); 83 | printf("system.interval_d: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 84 | cfg_loader.dump_to("system.interval_w", dur, true); 85 | printf("system.interval_w: sec %lld, nsec: %lld\n", static_cast(dur.sec), static_cast(dur.nsec)); 86 | } 87 | 88 | return 0; 89 | } -------------------------------------------------------------------------------- /sample/test.ini: -------------------------------------------------------------------------------- 1 | a.b.c1 = 123 # 整数 2 | a.b.c2 = 1.23 ; 小数 3 | 4 | [a : b:c:d] 5 | e.f1 = "123\0456" ; 可转义字符串 6 | 7 | e.f2 = '123\0456'; 不可转义字符串 8 | 9 | [d . c.b.a] 10 | e.f3 = 123456789 11 | 12 | [a] 13 | b.c3 = 带空格 的 字符 串 14 | 15 | arr = 1 16 | arr = 2 17 | arr = 3 18 | arr = 一坨屎 19 | arr = /usr/local/gcc-4.8.2 20 | 21 | 22 | bool = true 23 | bool = false 24 | bool = yes 25 | bool = no 26 | bool = enable 27 | bool = disable 28 | bool = 1 29 | bool = 0 30 | 31 | [system] 32 | interval_ns = 123 ns 33 | interval_us = 123 us 34 | interval_ms = 123ms 35 | interval_s = 123 36 | interval_m = 123m 37 | interval_h = 123hours 38 | interval_d = 123days 39 | interval_w = 123weeks 40 | --------------------------------------------------------------------------------