c++ boost parse ini file when containing multikeys - c++

I need to parse ini file using C++ with boost library. This file contains the multi keys. For example,
[section_1]
key_1=value_1
key_1=value_2
...
key_n=value_n
[section_2]
key1=value_1
key1=value_2
...
key_n=value_1
key_n=value_2
[]
...
[section_n]
...
I tried use the functional of boost library: the function boost::property_tree::ini_parser::read_ini(), but it can't contain the multikey in ini file and return the exception. So I tried use the function boost::program_options::parse_config_file(), but it's not what I need.
What functionality should I use to parse the ini file and for each section I can to get own structure with relevant key values?

Your input is simply not an INI file, as INI files do not permit duplicate values. You can write your own parser, e.g. using the code I wrote here:¹
Cross-platform way to get line number of an INI file where given option was found
If you replace the section_t map
typedef std::map<textnode_t, textnode_t> section_t;
with multimap:
typedef std::multimap<textnode_t, textnode_t> section_t;
you can parse repeated keys:
[section_1]
key_1=value_1
key_1=value_2
key_n=value_n
[section_2]
key1=value_1
key2=value_2
key_n=value_1
key_n=value_2
[section_n]
See full code here: https://gist.github.com/sehe/068b1ae81547b98a3cec02a530f220df
¹ or Learning Boost.Spirit: parsing INI and http://coliru.stacked-crooked.com/view?id=cd1d516ae0b19bd6f9af1e3f1b132211-0d2159870a1c6cb0cd1457b292b97230 and possibly others

A SSCCE that might help you
Live On Coliru
#include <boost/property_tree/ini_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::istringstream iss(R"([section_1]
key_1=value_1
key_2=value_2
key_n=value_n
[section_2]
key1=value_1
key2=value_2
key_n=value_n
key_m=value_m
[]
[section_n])");
ptree pt;
read_ini(iss, pt);
for (auto& section : pt) {
std::cout << "[" << section.first << "]\n";
for (auto& key : section.second) {
std::cout << key.first << "=" << key.second.get_value("") << "\n";
}
}
}
Prints
[section_1]
key_1=value_1
key_2=value_2
key_n=value_n
[section_2]
key1=value_1
key2=value_2
key_n=value_n
key_m=value_m

Related

Portable way to read a file in C++ and handle possible errors

I want to do a simple thing: read the first line from a file, and do a proper error reporting in case there is no such file, no permission to read the file and so on.
I considered the following options:
std::ifstream. Unfortunately, there is no portable way to report system errors. Some other answers suggest checking errno after reading failed, but the standard does not guarantee that errno is set by any functions in iostreams library.
C style fopen/fread/fclose. This works, but is not as convenient as iostreams with std::getline. I'm looking for C++ solution.
Is there any way to accomplish this using C++14 and boost?
Disclaimer: I am the author of AFIO. But exactly what you are looking for is https://ned14.github.io/afio/ which is the v2 library incorporating the feedback from its Boost peer review in August 2015. See the list of features here.
I will of course caveat that this is an alpha quality library, and you should not use it in production code. However, quite a few people already are doing so.
How to use AFIO to solve the OP's problem:
Note that AFIO is a very low level library, hence you have to type a lot more code to achieve the same as iostreams, on the other hand you get no memory allocation, no exception throwing, no unpredictable latency spikes:
// Try to read first line from file at path, returning no string if file does not exist,
// throwing exception for any other error
optional<std::string> read_first_line(filesystem::path path)
{
using namespace AFIO_V2_NAMESPACE;
// The result<T> is from WG21 P0762, it looks quite like an `expected<T, std::error_code>` object
// See Outcome v2 at https://ned14.github.io/outcome/ and https://lists.boost.org/boost-announce/2017/06/0510.php
// Open for reading the file at path using a null handle as the base
result<file_handle> _fh = file({}, path);
// If fh represents failure ...
if(!_fh)
{
// Fetch the error code
std::error_code ec = _fh.error();
// Did we fail due to file not found?
// It is *very* important to note that ec contains the *original* error code which could
// be POSIX, or Win32 or NT kernel error code domains. However we can always compare,
// via 100% C++ 11 STL, any error code to a generic error *condition* for equivalence
// So this comparison will work as expected irrespective of original error code.
if(ec == std::errc::no_such_file_or_directory)
{
// Return empty optional
return {};
}
std::cerr << "Opening file " << path << " failed with " << ec.message() << std::endl;
}
// If errored, result<T>.value() throws an error code failure as if `throw std::system_error(fh.error());`
// Otherwise unpack the value containing the valid file_handle
file_handle fh(std::move(_fh.value()));
// Configure the scatter buffers for the read, ideally aligned to a page boundary for DMA
alignas(4096) char buffer[4096];
// There is actually a faster to type shortcut for this, but I thought best to spell it out
file_handle::buffer_type reqs[] = {{buffer, sizeof(buffer)}};
// Do a blocking read from offset 0 possibly filling the scatter buffers passed in
file_handle::io_result<file_handle::buffers_type> _buffers_read = read(fh, {reqs, 0});
if(!_buffers_read)
{
std::error_code ec = _fh.error();
std::cerr << "Reading the file " << path << " failed with " << ec.message() << std::endl;
}
// Same as before, either throw any error or unpack the value returned
file_handle::buffers_type buffers_read(_buffers_read.value());
// Note that buffers returned by AFIO read() may be completely different to buffers submitted
// This lets us skip unnecessary memory copying
// Make a string view of the first buffer returned
string_view v(buffers_read[0].data, buffers_read[0].len);
// Sub view that view with the first line
string_view line(v.substr(0, v.find_first_of('\n')));
// Return a string copying the first line from the file, or all 4096 bytes read if no newline found.
return std::string(line);
}
People on boost-users mailing list pointed out that the boost.beast library has OS-independent API for basic file IO including proper error handling. There are three implementations of the file concept out-of-the-box: POSIX, stdio and win32. The implementations support RAII (automatic closing on destruction) and move semantics. The POSIX file model automatically handles EINTR error. Basically this is sufficient and convenient to portably read a file chunk by chunk and, for example, explicitly handle the situation of absence of a file:
using namespace boost::beast;
using namespace boost::system;
file f;
error_code ec;
f.open("/path/to/file", file_mode::read, ec);
if(ec == errc::no_such_file_or_directory) {
// ...
} else {
// ...
}
The best thing to do could be to wrap Boost WinAPI and or POSIX APIs.
The "naive" C++ standard library thing (with bells and wistles) doesn't get you too far:
Live On Coliru
#include <iostream>
#include <fstream>
#include <vector>
template <typename Out>
Out read_file(std::string const& path, Out out) {
std::ifstream s;
s.exceptions(std::ios::badbit | std::ios::eofbit | std::ios::failbit);
s.open(path, std::ios::binary);
return out = std::copy(std::istreambuf_iterator<char>{s}, {}, out);
}
void test(std::string const& spec) try {
std::vector<char> data;
read_file(spec, back_inserter(data));
std::cout << spec << ": " << data.size() << " bytes read\n";
} catch(std::ios_base::failure const& f) {
std::cout << spec << ": " << f.what() << " code " << f.code() << " (" << f.code().message() << ")\n";
} catch(std::exception const& e) {
std::cout << spec << ": " << e.what() << "\n";
};
int main() {
test("main.cpp");
test("nonexistent.cpp");
}
Prints...:
main.cpp: 823 bytes read
nonexistent.cpp: basic_ios::clear: iostream error code iostream:1 (iostream error)
Of course you can add more diagnostics perusing <filesystem> but
that's susceptible to races, as mentioned (depending on your application, these can even open up security vulnerabilities, so just say "No").
Using boost::filesystem::ifstream doesn't change the exceptions raised
Worse still, using Boost IOstream fails to raise any errors:
template <typename Out>
Out read_file(std::string const& path, Out out) {
namespace io = boost::iostreams;
io::stream<io::file_source> s;
s.exceptions(std::ios::badbit | std::ios::eofbit | std::ios::failbit);
s.open(path, std::ios::binary);
return out = std::copy(std::istreambuf_iterator<char>{s}, {}, out);
}
Happily prints:
main.cpp: 956 bytes read
nonexistent.cpp: 0 bytes read
Live On Coliru
#include <iostream>
#include <fstream>
#include <string>
#include <system_error>
using namespace std;
int
main()
{
ifstream f("testfile.txt");
if (!f.good()) {
error_code e(errno, system_category());
cerr << e.message();
//...
}
// ...
}
ISO C++ Standard:
The contents of the header
"cerrno"
are the same as the POSIX header
"errno.h"
, except that
errno
shall
be defined as a macro. [
Note:
The intent is to remain in close alignment with the POSIX standard.
— end
note
] A separate
errno
value shall be provided for each thread.
check this code:
uSTL is a partial implementation of the C++ standard library that focuses on
decreasing the memory footprint of user executables.
https://github.com/msharov/ustl/blob/master/fstream.cc

boost property tree cannot read multiple json data in one file

I really need to get help to decide my problem. I am using boost property tree to parse twitter messages that is stored in json file. All messages are saved in one json file and I need to parse all one by one.
Here is the twitter json data saved in a file. it has 3 different messages. (Below is deducted message only for test)
{"id":593393012970926082,"in_reply_to_status_id":1,"user":{"id":2292380240,"followers_count":2},"retweet_count":0}
{"id":654878454684687878,"in_reply_to_status_id":7,"user":{"id":2292380241,"followers_count":4},"retweet_count":5}
{"id":123487894154878414,"in_reply_to_status_id":343,"user":{"id":2292380242,"followers_count":773},"retweet_count":654}
And here is my C++ code for parsing the message, using property tree.
#include <boost/property_tree/json_parser.hpp>
using namespace std;
using namespace boost::property_tree;
string jsonfile = "./twitter.json";
int main()
{
ptree pt;
read_json( jsonfile, pt );
cout<<"in_reply_to_status_id: "<<pt.get("in_reply_to_status_id",0)<<"\n";
}
I want to get all in_reply_to_status_id values from the file. Now it is printing only the first line value. The result is printing follow.
in_reply_to_status_id: 1
I would like to get all values like below.
in_reply_to_status_id: 1
in_reply_to_status_id: 7
in_reply_to_status_id: 343
How can I get all values from the file.
Please help me. Thank you very much.
You should have right json file, for example like this
[
{"id":593393012970926082,"in_reply_to_status_id":1,"user":{"id":2292380240,"followers_count":2},"retweet_count":0},
{"id":654878454684687878,"in_reply_to_status_id":7,"user":{"id":2292380241,"followers_count":4},"retweet_count":5},
{"id":123487894154878414,"in_reply_to_status_id":343,"user":{"id":2292380242,"followers_count":773},"retweet_count":654}
]
And code should be like this
for (const auto& p : pt)
{
cout << p.second.get("in_reply_to_status_id",0) << endl;
}
Instead of range-based for, you can use BOOST_FOREACH for example.
BOOST_FOREACH(const ptree::value_type& p, pt)
You can see my example, first you should get the child tree, and then parse it. My code:
string str = "{\"key\":[{\"id\":1}, {\"id\":2}]}";
stringstream ss(str);
boost::property_tree::ptree parser, child;
boost::property_tree::json_parser::read_json(ss, parser);
child = parser.get_child("key");
for(auto& p : child)
cout << p.second.get<uint32_t>("id") << endl;
I hope this can help you.

Boost::ini_parser: What is the way to read specific section's values?

I am using boost::property_tree in order to read .ini file.
I know i can read specific key(inside section) -> iniTree.get<std::string>("section.key").
I know i can read all values in the ini file.
I want to read only keys from specific section.
Something like that: iniTree.get<std::vector<std::string> >("section").
Is it possible?
Yes. You use 'get_child' to get a subtree.
You can use get_child_optional in case you don't know before hand whether the section exists.
Here's a demo that shows both variations:
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <fstream>
#include <iostream>
using boost::property_tree::ptree;
int main() {
std::fstream ifs("input.txt");
ptree pt;
read_ini(ifs, pt);
// let's get just the section2
if (boost::optional<ptree&> oops = pt.get_child_optional("oops")) {
std::cout << "There is a section `oops`\n";
} else {
std::cout << "There is NO section `oops`\n";
}
ptree& sub1 = pt.get_child("section2"); // we want the CCs!
write_ini(std::cout, sub1);
}
And given an input.txt of:
[section1]
huh=value1
slam=value2
cram=value3
[section2]
rabbits=creditcard1
die=creditcard2
eagerly=creditcard3
It'll print the output:
There is NO section `oops`
rabbits=creditcard1
die=creditcard2
eagerly=creditcard3

Create .csv file in C++ in qt

I want to create an csv file using c++, using Qt for application and UI framework. Is there's library for csv file.
Try qtcsv library for reading and writing csv-files. Example:
#include <QList>
#include <QStringList>
#include <QDir>
#include <QDebug>
#include "qtcsv/stringdata.h"
#include "qtcsv/reader.h"
#include "qtcsv/writer.h"
int main()
{
// prepare data that you want to save to csv-file
QStringList strList;
strList << "one" << "two" << "three";
QtCSV::StringData strData;
strData.addRow(strList);
strData.addEmptyRow();
strData << strList << "this is the last row";
// write to file
QString filePath = QDir::currentPath() + "/test.csv";
QtCSV::Writer::write(filePath, strData);
// read data from file
QList<QStringList> readData = QtCSV::Reader::readToList(filePath);
for ( int i = 0; i < readData.size(); ++i )
{
qDebug() << readData.at(i).join(",");
}
return 0;
}
I tried to make it small and easy-to-use. See Readme file for library documentation and other code examples.
You could basically look into libqxt.
Using QxtCsvModel
The QxtCsvModel [libqxt.bitbucket.org] class provides a QAbstractTableModel [qt-project.org] for CSV Files. This is perhaps the easiest way possible to read and write csv files without having to parse the csv format to something qt can understand. It’s as simple as using one line of code, for example the following reads the csv file:
csvmodel->setSource(fileName);
Just writing CSV? Although google may reveal some CSV libraries, the probable reason why you have not found any is because it is so darn trivial. Remember CSV is just Comma Separated Values.
To implement it use any means to write a text file (std::ofstream, QFile, QTextStream) and do something along the lines of:
foreach record
{
foreach value in record
{
write "\"" + escape(value) + "\""
if not last value in record
{
write ","
}
}
write "\n"
}
escape (value)
{
replace each "\"" with "\"\""
}
Note that you can write the values without quotes if they do not contain any separators (,). Also note you can use different separators, for example the semi-colon is commonly used.

iterate over ini file on c++, probably using boost::property_tree::ptree?

My task is trivial - i just need to parse such file:
Apple = 1
Orange = 2
XYZ = 3950
But i do not know the set of available keys. I was parsing this file relatively easy using C#, let me demonstrate source code:
public static Dictionary<string, string> ReadParametersFromFile(string path)
{
string[] linesDirty = File.ReadAllLines(path);
string[] lines = linesDirty.Where(
str => !String.IsNullOrWhiteSpace(str) && !str.StartsWith("//")).ToArray();
var dict = lines.Select(s => s.Split(new char[] { '=' }))
.ToDictionary(s => s[0].Trim(), s => s[1].Trim());
return dict;
}
Now I just need to do the same thing using c++. I was thinking to use boost::property_tree::ptree however it seems I just can not iterate over ini file. It's easy to read ini file:
boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini(path, pt);
But it is not possible to iterate over it, refer to this question Boost program options - get all entries in section
The question is - what is the easiest way to write analog of C# code above on C++ ?
To answer your question directly: of course iterating a property tree is possible. In fact it's trivial:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
int main()
{
using boost::property_tree::ptree;
ptree pt;
read_ini("input.txt", pt);
for (auto& section : pt)
{
std::cout << '[' << section.first << "]\n";
for (auto& key : section.second)
std::cout << key.first << "=" << key.second.get_value<std::string>() << "\n";
}
}
This results in output like:
[Cat1]
name1=100 #skipped
name2=200 \#not \\skipped
name3=dhfj dhjgfd
[Cat_2]
UsagePage=9
Usage=19
Offset=0x1204
[Cat_3]
UsagePage=12
Usage=39
Offset=0x12304
I've written a very full-featured Inifile parser using boost-spirit before:
Cross-platform way to get line number of an INI file where given option was found
It supports comments (single line and block), quotes, escapes etc.
(as a bonus, it optionally records the exact source locations of all the parsed elements, which was the subject of that question).
For your purpose, though, I think I'd recomment Boost Property Tree.
For the moment, I've simplified the problem a bit, leaving out the logic for comments (which looks broken to me anyway).
#include <map>
#include <fstream>
#include <iostream>
#include <string>
typedef std::pair<std::string, std::string> entry;
// This isn't officially allowed (it's an overload, not a specialization) but is
// fine with every compiler of which I'm aware.
namespace std {
std::istream &operator>>(std::istream &is, entry &d) {
std::getline(is, d.first, '=');
std::getline(is, d.second);
return is;
}
}
int main() {
// open an input file.
std::ifstream in("myfile.ini");
// read the file into our map:
std::map<std::string, std::string> dict((std::istream_iterator<entry>(in)),
std::istream_iterator<entry>());
// Show what we read:
for (entry const &e : dict)
std::cout << "Key: " << e.first << "\tvalue: " << e.second << "\n";
}
Personally, I think I'd write the comment skipping as a filtering stream buffer, but for those unfamiliar with the C++ standard library, it's open to argument that would be a somewhat roundabout solution. Another possibility would be a comment_iterator that skips the remainder of a line, starting from a designated comment delimiter. I don't like that as well, but it's probably simpler in some ways.
Note that the only code we really write here is to read one, single entry from the file into a pair. The istream_iterator handles pretty much everything from there. As such, there's little real point in writing a direct analog of your function -- we just initialize the map from the iterators, and we're done.