-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIniFile.cpp
More file actions
339 lines (310 loc) · 12.3 KB
/
IniFile.cpp
File metadata and controls
339 lines (310 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#include "IniFile.h"
#include <fstream>
#include <iostream>
using namespace std;
void check_section(string &line) {
// В секции точно есть [], т.е. больше двух символов
if (line.length() <= 2) {
line.erase(0, line.length());
}
// Убираем секции вида ...[] и []...
if ((line[0] != '[') || (line[line.length() - 1] != ']')) {
line.erase(0, line.length());
}
// Убираем секции с ][
if (line.find(']') < line.find('[')) {
line.erase(0, line.length());
}
// Убираем секции, в которых много скобок
// Изначально присваиваем единички, т.к. мы знаем, что в строке есть [] из
// функции is_section, причем первый символ - [, последний - ]. Иначе мы бы
// обрезали строку в условии выше
int open_brackets = 1, close_brackets = 1;
for (size_t i = 1; i < line.length() - 1; i++) {
if (line[i] == '[') {
open_brackets++;
}
if (line[i] == ']') {
close_brackets++;
}
// В секции должны быть только буквы, цифры и точки
if ((!isalpha(line[i]) && !isdigit(line[i]) && line[i] != '.')) {
line.erase(0, line.length());
break;
}
if ((close_brackets != 1) || (open_brackets != 1)) {
line.erase(0, line.length());
break;
}
}
}
//Проверка, является ли строка секцией
int is_section(const string line) {
if ((line.find('[') != string::npos) || (line.find(']') != string::npos)) {
return 1; // Это секция, т.к. есть []
}
return 2; // Строка находится внутри секции
}
// Удаляем все пробелы в строке
void delete_spaces(string &line) {
int i;
while (line.find(' ') != string::npos) {
i = line.find(' ');
line.erase(i, 1);
}
}
// Удаляем комментарии в строке
void delete_comments(string &line) {
int i;
while (line.find(';') != string::npos) {
i = line.find(';');
line.erase(i, line.length() - i);
}
}
// Проверяем, является ли строка корректным ключом
void check_key(string &line) {
// В начале или в конце строки стоит '='
if ((line[0] == '=') || (line[line.length() - 1] == '=')) {
line.erase(0, line.length());
}
// Пройдёмся по строке, проверим, сколько в ней знаков '=', есть ли в ней
// что-то, кроме букв, цифр и точек
int tmp = 0;
for (size_t i = 0; i < line.length(); i++) {
if (line[i] == '=') {
tmp++;
}
if ((!isalpha(line[i])) && (!isdigit(line[i])) && (line[i] != '=') &&
(line[i] != '.')) {
line.erase(0, line.length());
break;
}
}
if (tmp != 1) {
line.erase(0, line.length());
}
}
// Обрабатываем строку и возвращаем тип строки
// 0 - пустая
// 1 - секция
// 2 - значение внутри секции
int check_line(string &line) {
delete_spaces(line); // Удаляем все пробелы в строке
delete_comments(line); // Удаляем комментарии
if (line.empty()) {
return 0; // Строка пустая
}
int tmp = is_section(line);
if (tmp == 1) { // Это секция
check_section(line);
} else if (tmp == 2) { //Внутри секции
check_key(line);
}
if (line.empty())
return 0; // Строка пустая
else if (tmp == 1)
return 1; // Строка - секция
else
return 2; // Строка - значение внутри секции
}
// Получаем имя секции, обрезая []
string §ion_name(string &line) {
line.erase(0, 1);
line.erase(line.size() - 1, 1);
return line;
}
IniFile::IniFile(const string &path) : filePath(path) {
ifstream f(filePath);
if (f.is_open()) {
string line, section, key, value;
KeysMap keys; // Временный контейнер с ключами и значениями
SectionsMap sections;
// Флаг нам нужен, чтобы не начать записывать пустоту до первой секции
// куда-то в космос.
bool flag = false;
int line_type;
// Пока в не дошли до конца файла, читаем его
while (getline(f, line)) {
line_type = check_line(line); // определяем тип строки
// Это секция
if (line_type == 1) {
// Если это не первая секция, которую мы нашли, то
if (flag) {
// Если мы ничего не попутали и она действительно есть
if (data.find(section) != data.end()) {
// Проходимся по всей секции
for (const auto &element : data[section]) {
// Если у нас есть ключ, которого до этого не было
if (keys.find(element.first) == keys.end()) {
// То записываем значение по этому ключу в нашу секцию
keys[element.first] = element.second;
}
}
}
// Вываливаем временный контейнер в нашу data
data[section] = keys;
// Очищаем временный контейнер
keys.clear();
}
// Запоминаем имя секции, в которую зашли
section = section_name(line);
if (!flag) {
flag = true;
}
}
//Это ключ со значением
if (line_type == 2) {
int tmp = line.find('=');
// Разделяем строку на ключ и значение
key = line.substr(0, tmp);
value = line.substr(tmp + 1, line.length());
// Добавляем во временный контейнер значение по ключу
keys[key] = value;
// Очищаем наши временные ключ и значение
key.clear();
value.clear();
}
}
// Записываем последнюю секцию, т.к. в while() мы делаем этого
if (data.find(section) != data.end()) {
for (const auto &element : data[section]) {
if (keys.find(element.first) == keys.end()) {
keys[element.first] = element.second;
}
}
}
data[section] = keys;
keys.clear();
}
}
/// Деструктор
/// @note Должен перезаписывать файл кешированой конифгурацией
IniFile::~IniFile() {
save();
}
/// Запись кешированной конфигурации в файл
void IniFile::save() {
ofstream file(filePath);
if (file.is_open()) {
for (const auto §ions : data) {
file << '[' << sections.first << ']' << '\n';
for (const auto &keys : sections.second) {
file << keys.first << '=' << keys.second << '\n';
}
}
} else {
throw "Error";
}
}
/// Чтение значения типа int
/// @param section Секция ini-файла
/// @param key Ключ в секции
/// @param def Значение по умолчанию, возвращаемое в случае отсутсвия
/// ключа/секции
int IniFile::readInt(const string §ion, const string &key, int def) {
if ((isSectionExist(section) == true) &&
(isKeysExist(section, key) == true)) {
return stoi(data[section][key]);
}
return def;
}
// Чтение значения типа double
double IniFile::readDouble(const string §ion, const string &key,
double def) {
if ((isSectionExist(section) == true) &&
(isKeysExist(section, key) == true)) {
return stod(data[section][key]);
}
return def;
}
// Чтение значения типа std::string
string IniFile::readString(const std::string §ion, const std::string &key,
const std::string &def) {
if ((isSectionExist(section) == true) && (isSectionExist(section) == true)) {
return data[section][key];
}
return def;
}
// Чтение значения типа bool
// При чтении строка должна сопоставляться со всеми значениям из trueValues,
// независимо от регистра В остальные случаях - вовзращается false
bool IniFile::readBool(const std::string §ion, const std::string &key,
bool def) {
if (isSectionExist(section) && isKeysExist(section, key)) {
string str = data[section][key];
for (auto element : trueValues) {
if (str == element) {
return true;
}
}
}
return def;
}
// Запись значения типа int
void IniFile::writeInt(const std::string §ion, const std::string &key,
int value) {
data[section][key] = to_string(value);
}
// Запись значения типа double
void IniFile::writeDouble(const std::string §ion, const std::string &key,
double value) {
data[section][key] = to_string(value);
}
// Запись значения типа std::string
void IniFile::writeString(const std::string §ion, const std::string &key,
const std::string &value) {
data[section][key] = value;
}
// Наша вспомогательная функция
string boolToString(bool value) { return value ? "true" : "false"; }
// Запись значения типа bool
void IniFile::writeBool(const std::string §ion, const std::string &key,
bool value) {
data[section][key] = boolToString(value);
}
// Проверка существования секции
bool IniFile::isSectionExist(const std::string §ion) {
return data.find(section) != data.end();
}
// Проверка существования ключа в секции
bool IniFile::isKeysExist(const std::string §ion, const std::string &key) {
if (data.find(section) != data.end()) {
return data[section].find(key) != data[section].end();
}
return false;
}
// Получение количества секций
size_t IniFile::getSectionsCount() { return data.size(); }
// Получение количества ключей в секции
size_t IniFile::getKeysCount(const std::string §ion) {
if (isSectionExist(section)) {
return data[section].size();
} else {
return 0;
}
}
// Получение значений всех секций
SectionsMap IniFile::getSections() const { return data; }
// Удаление секции
bool IniFile::deleteSection(const string §ion) {
if (data.find(section) == data.end()) {
return false;
}
return data.erase(section) == 1;
}
// Удаление ключа из секции
bool IniFile::deleteKey(const std::string §ion, const std::string &key) {
if (isSectionExist(section)) {
return data[section].erase(key) == 1;
} else {
return false;
}
}
// Добавление новой секции
bool IniFile::addNewSection(const std::string §ion) {
if (isSectionExist(section) == false) {
data[section] = {};
return true;
}
return false;
}