90#include <unordered_map>
99 namespace INIStringUtil
101 const char *
const whitespaceDelimiters =
" \t\n\r\f\v";
102 inline void trim(std::string &str)
104 str.erase(str.find_last_not_of(whitespaceDelimiters) + 1);
105 str.erase(0, str.find_first_not_of(whitespaceDelimiters));
107#ifndef MINI_CASE_SENSITIVE
110 inline void toLower(std::string &str)
112 std::transform(str.begin(), str.end(), str.begin(), [](
const char c)
113 { return static_cast<char>(std::tolower(c)); });
120 inline void replace(std::string &str, std::string
const &a, std::string
const &b)
125 while ((pos = str.find(a, pos)) != std::string::npos)
127 str.replace(pos, a.size(), b);
133 const char *
const endl =
"\r\n";
135 const char *
const endl =
"\n";
141 template <
typename T>
145 using T_DataIndexMap = std::unordered_map<std::string, std::size_t>;
146 using T_DataItem = std::pair<std::string, T>;
147 using T_DataContainer = std::vector<T_DataItem>;
148 using T_MultiArgs =
typename std::vector<std::pair<std::string, T>>;
150 T_DataIndexMap dataIndexMap;
151 T_DataContainer data;
153 inline std::size_t setEmpty(std::string &key)
155 std::size_t index = data.
size();
156 dataIndexMap[key] = index;
157 data.emplace_back(key, T());
170 INIMap(
INIMap const &other) : dataIndexMap(other.dataIndexMap), data(other.data)
179 INIStringUtil::trim(key);
180#ifndef MINI_CASE_SENSITIVE
181 INIStringUtil::toLower(key);
183 auto it = dataIndexMap.find(key);
184 bool hasIt = (it != dataIndexMap.end());
185 std::size_t index = (hasIt) ? it->second : setEmpty(key);
186 return data[index].second;
192 T
get(std::string key)
const
194 INIStringUtil::trim(key);
195#ifndef MINI_CASE_SENSITIVE
196 INIStringUtil::toLower(key);
198 auto it = dataIndexMap.find(key);
199 if (it == dataIndexMap.end())
203 return T(data[it->second].second);
209 bool has(std::string key)
const
211 INIStringUtil::trim(key);
212#ifndef MINI_CASE_SENSITIVE
213 INIStringUtil::toLower(key);
215 return (dataIndexMap.count(key) == 1);
221 void set(std::string key, T obj)
223 INIStringUtil::trim(key);
224#ifndef MINI_CASE_SENSITIVE
225 INIStringUtil::toLower(key);
227 auto it = dataIndexMap.find(key);
228 if (it != dataIndexMap.end())
230 data[it->second].second = obj;
234 dataIndexMap[key] = data.size();
235 data.emplace_back(key, obj);
241 void set(T_MultiArgs
const &multiArgs)
243 for (
auto const &it : multiArgs)
245 auto const &key = it.first;
246 auto const &obj = it.second;
256 INIStringUtil::trim(key);
257#ifndef MINI_CASE_SENSITIVE
258 INIStringUtil::toLower(key);
260 auto it = dataIndexMap.find(key);
261 if (it != dataIndexMap.end())
263 std::size_t index = it->second;
264 data.erase(data.begin() + index);
265 dataIndexMap.erase(it);
266 for (
auto &it2 : dataIndexMap)
268 auto &vi = it2.second;
283 dataIndexMap.clear();
302 using INIStructure = INIMap<INIMap<std::string>>;
306 using T_ParseValues = std::pair<std::string, std::string>;
308 enum class PDataType :
char
321 inline PDataType parseLine(std::string line, T_ParseValues &parseData)
323 parseData.first.clear();
324 parseData.second.clear();
325 INIStringUtil::trim(line);
328 return PDataType::PDATA_NONE;
330 char firstCharacter = line[0];
331 if (firstCharacter ==
';')
333 return PDataType::PDATA_COMMENT;
335 if (firstCharacter ==
'[')
337 auto commentAt = line.find_first_of(
';');
338 if (commentAt != std::string::npos)
340 line = line.substr(0, commentAt);
342 auto closingBracketAt = line.find_last_of(
']');
343 if (closingBracketAt != std::string::npos)
345 auto section = line.substr(1, closingBracketAt - 1);
346 INIStringUtil::trim(section);
347 parseData.first = section;
348 return PDataType::PDATA_SECTION;
351 auto lineNorm = line;
352 INIStringUtil::replace(lineNorm,
"\\=",
" ");
353 auto equalsAt = lineNorm.find_first_of(
'=');
354 if (equalsAt != std::string::npos)
356 auto key = line.substr(0, equalsAt);
357 INIStringUtil::trim(key);
358 INIStringUtil::replace(key,
"\\=",
"=");
359 auto value = line.substr(equalsAt + 1);
360 INIStringUtil::trim(value);
361 parseData.first = key;
362 parseData.second = value;
363 return PDataType::PDATA_KEYVALUE;
365 return PDataType::PDATA_UNKNOWN;
382 std::ifstream fileReadStream;
389 fileReadStream.seekg(0, std::ios::end);
390 const std::size_t fileSize =
static_cast<std::size_t
>(fileReadStream.tellg());
391 fileReadStream.seekg(0, std::ios::beg);
394 const char header[3] = {
395 static_cast<char>(fileReadStream.get()),
396 static_cast<char>(fileReadStream.get()),
397 static_cast<char>(fileReadStream.get())};
398 isBOM = (header[0] ==
static_cast<char>(0xEF) &&
399 header[1] ==
static_cast<char>(0xBB) &&
400 header[2] ==
static_cast<char>(0xBF));
406 std::string fileContents;
407 fileContents.resize(fileSize);
408 fileReadStream.seekg(
isBOM ? 3 : 0, std::ios::beg);
409 fileReadStream.read(&fileContents[0], fileSize);
410 fileReadStream.close();
418 for (std::size_t i = 0; i < fileSize; ++i)
420 char &c = fileContents[i];
423 output.emplace_back(buffer);
427 if (c !=
'\0' && c !=
'\r')
432 output.emplace_back(buffer);
440 INIReader(std::string
const &filename,
bool keepLineData =
false)
442 fileReadStream.open(filename, std::ios::in | std::ios::binary);
445 lineData = std::make_shared<T_LineData>();
456 if (!fileReadStream.is_open())
462 bool inSection =
false;
463 INIParser::T_ParseValues parseData;
464 for (
auto const &line : fileLines)
466 auto parseResult = INIParser::parseLine(line, parseData);
467 if (parseResult == INIParser::PDataType::PDATA_SECTION)
470 data[section = parseData.first];
472 else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE)
474 auto const &key = parseData.first;
475 auto const &value = parseData.second;
476 data[section][key] = value;
478 if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN)
480 if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection)
484 lineData->emplace_back(line);
502 std::ofstream fileWriteStream;
512 fileWriteStream.open(filename, std::ios::out | std::ios::binary);
522 if (!fileWriteStream.is_open())
530 auto it = data.
begin();
533 auto const §ion = it->first;
534 auto const &collection = it->second;
539 if (collection.size())
541 fileWriteStream << INIStringUtil::endl;
542 auto it2 = collection.begin();
545 auto key = it2->first;
546 INIStringUtil::replace(key,
"=",
"\\=");
547 auto value = it2->second;
548 INIStringUtil::trim(value);
553 if (++it2 == collection.end())
557 fileWriteStream << INIStringUtil::endl;
560 if (++it == data.
end())
564 fileWriteStream << INIStringUtil::endl;
567 fileWriteStream << INIStringUtil::endl;
578 using T_LineData = std::vector<std::string>;
579 using T_LineDataPtr = std::shared_ptr<T_LineData>;
581 std::string filename;
586 INIParser::T_ParseValues parseData;
587 std::string sectionCurrent;
588 bool parsingSection =
false;
589 bool continueToNextSection =
false;
590 bool discardNextEmpty =
false;
591 bool writeNewKeys =
false;
592 std::size_t lastKeyLine = 0;
593 for (
auto line = lineData->begin(); line != lineData->end(); ++line)
597 auto parseResult = INIParser::parseLine(*line, parseData);
598 if (parseResult == INIParser::PDataType::PDATA_SECTION)
603 parsingSection =
false;
607 sectionCurrent = parseData.first;
608 if (data.
has(sectionCurrent))
610 parsingSection =
true;
611 continueToNextSection =
false;
612 discardNextEmpty =
false;
613 output.emplace_back(*line);
614 lastKeyLine = output.size();
618 continueToNextSection =
true;
619 discardNextEmpty =
true;
623 else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE)
625 if (continueToNextSection)
629 if (data.
has(sectionCurrent))
631 auto &collection = data[sectionCurrent];
632 auto const &key = parseData.first;
633 auto const &value = parseData.second;
634 if (collection.has(key))
636 auto outputValue = collection[key];
637 if (value == outputValue)
639 output.emplace_back(*line);
643 INIStringUtil::trim(outputValue);
644 auto lineNorm = *line;
645 INIStringUtil::replace(lineNorm,
"\\=",
" ");
646 auto equalsAt = lineNorm.find_first_of(
'=');
647 auto valueAt = lineNorm.find_first_not_of(
648 INIStringUtil::whitespaceDelimiters,
650 std::string outputLine = line->substr(0, valueAt);
655 outputLine += outputValue;
656 output.emplace_back(outputLine);
658 lastKeyLine = output.size();
664 if (discardNextEmpty && line->empty())
666 discardNextEmpty =
false;
668 else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN)
670 output.emplace_back(*line);
674 if (writeNewKeys || std::next(line) == lineData->end())
676 T_LineData linesToAdd;
677 if (data.
has(sectionCurrent) && original.
has(sectionCurrent))
679 auto const &collection = data[sectionCurrent];
680 auto const &collectionOriginal = original[sectionCurrent];
681 for (
auto const &it : collection)
684 if (collectionOriginal.has(key))
688 auto value = it.second;
689 INIStringUtil::replace(key,
"=",
"\\=");
690 INIStringUtil::trim(value);
691 linesToAdd.emplace_back(
695 if (!linesToAdd.empty())
698 output.begin() + lastKeyLine,
704 writeNewKeys =
false;
709 for (
auto const &it : data)
711 auto const §ion = it.first;
712 if (original.
has(section))
716 if (
prettyPrint && output.size() > 0 && !output.back().empty())
718 output.emplace_back();
720 output.emplace_back(
"[" + section +
"]");
721 auto const &collection = it.second;
722 for (
auto const &it2 : collection)
724 auto key = it2.first;
725 auto value = it2.second;
726 INIStringUtil::replace(key,
"=",
"\\=");
727 INIStringUtil::trim(value);
754 bool fileExists = (stat(filename.c_str(), &buf) == 0);
759 return generator << data;
762 T_LineDataPtr lineData;
763 bool readSuccess =
false;
764 bool fileIsBOM =
false;
767 if ((readSuccess = reader >> originalData))
770 fileIsBOM = reader.
isBOM;
777 T_LineData output = getLazyOutput(lineData, data, originalData);
778 std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary);
779 if (fileWriteStream.is_open())
783 const char utf8_BOM[3] = {
784 static_cast<char>(0xEF),
785 static_cast<char>(0xBB),
786 static_cast<char>(0xBF)};
787 fileWriteStream.write(utf8_BOM, 3);
791 auto line = output.begin();
794 fileWriteStream << *line;
795 if (++line == output.end())
799 fileWriteStream << INIStringUtil::endl;
812 std::string filename;
834 if (filename.empty())
839 return reader >> data;
848 if (filename.empty())
854 return generator << data;
863 if (filename.empty())
869 return writer << data;
INIFile class.
Definition ini.h:810
bool read(INIStructure &data) const
Clears "data" contents and reads INI file.
Definition ini.h:828
bool generate(INIStructure const &data, bool pretty=false) const
Generate INI structure into data.
Definition ini.h:846
~INIFile()
Destructor.
Definition ini.h:823
INIFile(std::string const &filename)
Constructor.
Definition ini.h:817
bool write(INIStructure &data, bool pretty=false) const
Writes INI structure to INI file.
Definition ini.h:861
INIGenerator class.
Definition ini.h:500
bool prettyPrint
Beautifier flag.
Definition ini.h:506
bool operator<<(INIStructure const &data)
Write generated from "data" INI file.
Definition ini.h:520
INIGenerator(std::string const &filename)
Constructor.
Definition ini.h:510
~INIGenerator()
Destructor.
Definition ini.h:515
INIMap class.
Definition ini.h:143
T get(std::string key) const
Gets Value of Key.
Definition ini.h:192
typename T_DataContainer::const_iterator const_iterator
Iterator for T_DataContainer.
Definition ini.h:163
void set(T_MultiArgs const &multiArgs)
Adds or modificating an existing Key with value Obj.
Definition ini.h:241
const_iterator end() const
Points the last item of data.
Definition ini.h:299
const_iterator begin() const
Points the beginning item of data.
Definition ini.h:295
bool has(std::string key) const
Check if Key exists.
Definition ini.h:209
INIMap()
Constructor.
Definition ini.h:166
bool remove(std::string key)
Remove Key and its value from dataIndexMap.
Definition ini.h:254
T & operator[](std::string key)
Array item access operator.
Definition ini.h:177
void set(std::string key, T obj)
Adds or modificating an existing Key with value Obj.
Definition ini.h:221
void clear()
Clears all the data.
Definition ini.h:280
INIMap(INIMap const &other)
Constructor.
Definition ini.h:170
std::size_t size() const
Returns number of pairs.
Definition ini.h:288
INIReader class.
Definition ini.h:371
~INIReader()
Destructor.
Definition ini.h:449
bool operator>>(INIStructure &data)
Reads INI file contents into "data" INI file structure.
Definition ini.h:454
T_LineDataPtr getLines()
Returns contents of INI file.
Definition ini.h:492
INIReader(std::string const &filename, bool keepLineData=false)
Constructor.
Definition ini.h:440
bool isBOM
BOM flag, indicetes, that indicates, that it is UTF-8.
Definition ini.h:379
std::vector< std::string > T_LineData
Vector of strings to store INI file structure into.
Definition ini.h:374
std::shared_ptr< T_LineData > T_LineDataPtr
Shared pointer to T_LineData.
Definition ini.h:376
INIWriter class.
Definition ini.h:576
bool prettyPrint
Beautifier.
Definition ini.h:737
bool operator<<(INIStructure &data)
Write prepared INI structure "data" to INI file.
Definition ini.h:751
INIWriter(std::string const &filename)
Constructor.
Definition ini.h:741
~INIWriter()
Destructor.
Definition ini.h:746