added INI parser
This commit is contained in:
parent
f396b8afa1
commit
f462d71200
|
@ -29,7 +29,8 @@ SOURCES += \
|
|||
io/ansiescapecodes.cpp \
|
||||
misc/random.cpp \
|
||||
io/bitreader.cpp \
|
||||
application/commandlineutils.cpp
|
||||
application/commandlineutils.cpp \
|
||||
io/inifile.cpp
|
||||
|
||||
HEADERS += \
|
||||
application/global.h \
|
||||
|
@ -54,7 +55,8 @@ HEADERS += \
|
|||
misc/memory.h \
|
||||
misc/random.h \
|
||||
io/bitreader.h \
|
||||
application/commandlineutils.h
|
||||
application/commandlineutils.h \
|
||||
io/inifile.h
|
||||
|
||||
OTHER_FILES += \
|
||||
README.md \
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
#include "inifile.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace IoUtilities {
|
||||
|
||||
/*!
|
||||
* \brief Parses all data from the specified \a inputStream.
|
||||
*/
|
||||
void IniFile::parse(std::istream &inputStream)
|
||||
{
|
||||
// define variable for state machine
|
||||
enum State {Init, Comment, ScopeName, Key, Value} state = Init;
|
||||
// current character
|
||||
char c;
|
||||
// number of postponed whitespaces
|
||||
unsigned int whitespace = 0;
|
||||
// current scope, key and value
|
||||
string scope, key, value;
|
||||
scope.reserve(16);
|
||||
key.reserve(16);
|
||||
value.reserve(256);
|
||||
// define actions for state machine
|
||||
// called when key/value pair is complete
|
||||
auto finishKeyValue = [&scope, &key, &value, &whitespace, this] {
|
||||
m_data[scope].insert(make_pair(key, value));
|
||||
key.clear();
|
||||
value.clear();
|
||||
whitespace = 0;
|
||||
};
|
||||
// called to add current character to current key or value
|
||||
auto addChar = [&whitespace, &c] (string &to) {
|
||||
if(c == ' ') {
|
||||
++whitespace;
|
||||
} else {
|
||||
if(!to.empty()) {
|
||||
while(whitespace) {
|
||||
to += ' ';
|
||||
--whitespace;
|
||||
}
|
||||
} else {
|
||||
whitespace = 0;
|
||||
}
|
||||
to += c;
|
||||
}
|
||||
};
|
||||
// thorw an exception when an IO error occurs
|
||||
inputStream.exceptions(ios_base::failbit | ios_base::badbit);
|
||||
// parse the file char by char
|
||||
try {
|
||||
while(inputStream.get(c)) {
|
||||
switch(state) {
|
||||
case Init:
|
||||
switch(c) {
|
||||
case '\n':
|
||||
break;
|
||||
case '#':
|
||||
state = Comment;
|
||||
break;
|
||||
case '=':
|
||||
whitespace = 0;
|
||||
state = Value;
|
||||
break;
|
||||
case '[':
|
||||
scope.clear();
|
||||
state = ScopeName;
|
||||
break;
|
||||
default:
|
||||
addChar(key);
|
||||
state = Key;
|
||||
}
|
||||
break;
|
||||
case Key:
|
||||
switch(c) {
|
||||
case '\n':
|
||||
finishKeyValue();
|
||||
state = Init;
|
||||
break;
|
||||
case '#':
|
||||
finishKeyValue();
|
||||
state = Comment;
|
||||
break;
|
||||
case '=':
|
||||
whitespace = 0;
|
||||
state = Value;
|
||||
break;
|
||||
default:
|
||||
addChar(key);
|
||||
}
|
||||
break;
|
||||
case Comment:
|
||||
switch(c) {
|
||||
case '\n':
|
||||
state = Init;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
break;
|
||||
case ScopeName:
|
||||
switch(c) {
|
||||
case ']':
|
||||
state = Init;
|
||||
break;
|
||||
default:
|
||||
scope += c;
|
||||
}
|
||||
break;
|
||||
case Value:
|
||||
switch(c) {
|
||||
case '\n':
|
||||
finishKeyValue();
|
||||
state = Init;
|
||||
break;
|
||||
case '#':
|
||||
finishKeyValue();
|
||||
state = Comment;
|
||||
break;
|
||||
default:
|
||||
addChar(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (const ios_base::failure &) {
|
||||
if(inputStream.eof()) {
|
||||
// we just reached the end of the file
|
||||
// don't forget to save the last key/value pair
|
||||
finishKeyValue();
|
||||
} else {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Write the current data to the specified \a outputStream.
|
||||
*/
|
||||
void IniFile::make(ostream &outputStream)
|
||||
{
|
||||
// thorw an exception when an IO error occurs
|
||||
outputStream.exceptions(ios_base::failbit | ios_base::badbit);
|
||||
for(const auto &scope : m_data) {
|
||||
outputStream << '[' << scope.first << ']' << '\n';
|
||||
for(const auto &field : scope.second) {
|
||||
outputStream << field.first << '=' << field.second << '\n';
|
||||
}
|
||||
outputStream << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace IoUtilities
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef IOUTILITIES_INIFILE_H
|
||||
#define IOUTILITIES_INIFILE_H
|
||||
|
||||
#include "../application/global.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace IoUtilities {
|
||||
|
||||
class LIB_EXPORT IniFile
|
||||
{
|
||||
public:
|
||||
IniFile();
|
||||
|
||||
std::map<std::string, std::multimap<std::string, std::string> > &data();
|
||||
const std::map<std::string, std::multimap<std::string, std::string> > &data() const;
|
||||
void parse(std::istream &inputStream);
|
||||
void make(std::ostream &outputStream);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::multimap<std::string, std::string> > m_data;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Constructs an empty ini file.
|
||||
*/
|
||||
inline IniFile::IniFile()
|
||||
{}
|
||||
|
||||
/*!
|
||||
* \brief Returns the data of the file.
|
||||
*
|
||||
* - The keys in the returned map represent the [scope name]s.
|
||||
* - The values in the returned map are maps representing "key = value"-pairs within the scope.
|
||||
* - The data might be modified an saved using the make() method.
|
||||
*/
|
||||
inline std::map<std::string, std::multimap<std::string, std::string> > &IniFile::data()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the data of the file.
|
||||
*
|
||||
* - The keys in the returned map represent the [scope name]s.
|
||||
* - The values in the returned map are maps representing "key = value"-pairs within the scope.
|
||||
*/
|
||||
inline const std::map<std::string, std::multimap<std::string, std::string> > &IniFile::data() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
} // namespace IoUtilities
|
||||
|
||||
#endif // IOUTILITIES_INIFILE_H
|
Loading…
Reference in New Issue