We can see that your source data follows a certain pattern.
There is a "key" in brackets []
and a "value" after that, followed by a semicolon ;
If we have such a clear pattern, then we can use simple mechanisms. In "formal language" theory, we call this a "Type-3 grammar" or "Regular grammar" according to the Chomsky hierarchy definitions. Please read here about that.
The good point is: You do not need a parser. You can survive with so called regular expressions in such cases.
And, C++ supports regular expressions with the regex library.
I will use the following regex to match the above described pattern: \[([^\]]+)\]\s+([^; ]+);
If you paste this in a regex-online tool like regex101, then you can test it. The description is:
[([^]]+)]\s+([^; ]+);
\[([^\]]+)\]\s+([^; ]+);
\[ matches the character [ with index 9110 (5B16 or 1338) literally (case sensitive)
1st Capturing Group ([^\]]+)
Match a single character not present in the list below [^\]]
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
\] matches the character ] with index 9310 (5D16 or 1358) literally (case sensitive)
\] matches the character ] with index 9310 (5D16 or 1358) literally (case sensitive)
\s
matches any whitespace character (equivalent to [\r\n\t\f\v ])
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
2nd Capturing Group ([^; ]+)
Match a single character not present in the list below [^; ]
+ matches the previous token between one and unlimited times, as many times as possible, giving back as needed (greedy)
;
matches a single character in the list ; (case sensitive)
; matches the character ; with index 5910 (3B16 or 738) literally (case sensitive)
Next:
We will use a std::map
to store the data. We can use its map-operator, to store and to access values for keys.
If you want to store a new value, then you can use for example config["ServerAPPsharename"] = "LR520APP"
. And, if you want to access a value via a key, then you could write std::cout << config["ServerAPPsharename"];
We can simply iterate over all matches (as long as there are any) and store the data in the std::map
. We will use regex_search for that.
Then we can easily access all data.
Please see the below code as one of many many possible solutions.
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <map>
#include <iomanip>
std::regex re{R"(\[([^\]]+)\]\s+([^; ]+);)"};
const std::string configFileName{ "test.txt" };
int main() {
// Open the source file with config data and check, if it could be opened successfully
if (std::ifstream configFileStream{ configFileName }; configFileStream) {
// Load the complete content of the file in this string
std::string configData(std::istreambuf_iterator<char>(configFileStream), {});
// We will use an associative container to store the key and its value.
std::map<std::string, std::string> config{};
// Now iterate over all pattern matches and store the data in our map
for (std::smatch sm; std::regex_search(configData, sm, re); configData = sm.suffix())
config[sm[1]] = sm[2];
// Example: show value for ServerAPPsharename
std::cout << "ServerAPPsharename: " << config["ServerAPPsharename"] << "\n\n";
// show all config data
std::cout << "\nAll data:\n\n";
for (const auto& [key, value] : config) std::cout << std::setw(20) << key << ": " << value << '\n';
}
else { // Error, could not open source file. Show message
std::cerr << "\nError: Could not open '" << configFileName << "'\n\n";
}
return 0;
}