37 #include <type_traits>
42 namespace io_dsv_impl {
45 template<
char Delimiter>
61 const std::vector<std::string>& columns,
const std::string& path,
62 int precision = std::numeric_limits<double>::max_digits10);
72 template<
typename Arg0,
typename... Args>
73 void append(Arg0&& arg0, Args&&... args);
82 std::is_arithmetic<std::decay_t<T>>
::value
86 template<
typename T,
typename Allocator>
87 static unsigned write(
const std::vector<T, Allocator>& xs, std::ostream& os);
91 template<
char Delimiter>
110 bool read(std::vector<std::string>& columns);
122 template<
char Delimiter,
typename NamedTuple>
137 const std::string& path,
138 int precision = std::numeric_limits<double>::max_digits10)
144 record, std::make_index_sequence<
152 const auto& from_record = NamedTuple::names();
153 return {from_record.begin(), from_record.end()};
155 template<std::size_t...
I>
168 std::istringstream is(str);
182 template<
char Delimiter,
typename NamedTuple>
201 const std::string& path,
202 const std::vector<std::string>& optional_columns = {},
203 bool verify_header =
true);
213 bool read(NamedTuple& record);
220 bool read(NamedTuple& record, std::vector<T>& extra);
229 using Tuple =
typename NamedTuple::Tuple;
241 void parse_header(
const std::vector<std::string>& optional_columns);
242 template<std::size_t...
I>
243 void parse_record(NamedTuple& record, std::index_sequence<I...>)
const {
246 using Vacuum =
int[];
247 (
void)Vacuum{(parse_element<I>(record), 0)...};
249 template<std::
size_t I>
260 template<
char Delimiter>
262 const std::vector<std::string>& columns,
const std::string& path,
265 path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc)
266 , m_num_columns(columns.size()) {
268 throw std::runtime_error(
"Could not open file '" + path +
"'");
270 m_file.precision(precision);
272 throw std::invalid_argument(
"No columns were specified");
278 template<
char Delimiter>
279 template<
typename Arg0,
typename... Args>
284 std::stringstream line;
286 line.precision(m_file.precision());
287 unsigned written_columns[] = {
289 write(std::forward<Arg0>(arg0), line),
295 (line << Delimiter, write(std::forward<Args>(args), line))...,
299 unsigned total_columns = 0;
300 for (
auto nc : written_columns) {
303 if (total_columns < m_num_columns) {
304 throw std::invalid_argument(
"Not enough columns");
306 if (m_num_columns < total_columns) {
307 throw std::invalid_argument(
"Too many columns");
310 m_file << line.rdbuf();
311 if (not m_file.good()) {
312 throw std::runtime_error(
"Could not write data to file");
316 template<
char Delimiter>
319 std::is_arithmetic<std::decay_t<T>>
::value
327 template<
char Delimiter>
328 template<
typename T,
typename Allocator>
331 const std::vector<T, Allocator>& xs, std::ostream& os) {
333 for (
const auto&
x : xs) {
345 template<
char Delimiter>
347 : m_file(path, std::ios_base::binary | std::ios_base::
in) {
349 throw std::runtime_error(
"Could not open file '" + path +
"'");
353 template<
char Delimiter>
357 std::getline(m_file, m_line);
362 throw std::runtime_error(
369 for (std::string::size_type
pos = 0;
pos < m_line.size();) {
370 auto del = m_line.find_first_of(Delimiter,
pos);
371 if (del == std::string::npos) {
373 columns.emplace_back(m_line,
pos);
376 columns.emplace_back(m_line,
pos, del -
pos);
386 template<
char Delimiter,
typename NamedTuple>
388 const std::string& path,
const std::vector<std::string>& optional_columns,
392 if ((not optional_columns.empty()) and (not verify_header)) {
393 throw std::runtime_error(
394 "Optional columns can not be used without header verification");
398 throw std::runtime_error(
"Could not read header from '" + path +
"'");
407 template<
char Delimiter,
typename NamedTuple>
410 if (not m_reader.read(m_columns)) {
414 if (m_columns.size() < m_num_columns) {
415 throw std::runtime_error(
416 "Too few columns in line " +
std::to_string(m_reader.num_lines()));
418 if (m_num_columns < m_columns.size()) {
419 throw std::runtime_error(
420 "Too many columns in line " +
std::to_string(m_reader.num_lines()));
428 template<
char Delimiter,
typename NamedTuple>
432 NamedTuple& record, std::vector<T>& extra) {
434 if (not read(record)) {
438 extra.resize(m_extra_columns.size());
439 for (std::size_t i = 0; i < m_extra_columns.size(); ++i) {
440 parse(m_columns[m_extra_columns[i]], extra[i]);
445 template<
char Delimiter,
typename NamedTuple>
450 for (std::size_t i = 0; i < m_tuple_column_map.size(); ++i) {
451 m_tuple_column_map[i] = i;
454 m_extra_columns.clear();
457 template<
char Delimiter,
typename NamedTuple>
460 const std::vector<std::string>& optional_columns) {
461 const auto& names = NamedTuple::names();
464 m_num_columns = m_columns.size();
467 for (
const auto&
name : names) {
469 auto o = std::find(optional_columns.begin(), optional_columns.end(),
name);
470 if (o != optional_columns.end()) {
474 auto c = std::find(m_columns.begin(), m_columns.end(),
name);
475 if (
c == m_columns.end()) {
476 throw std::runtime_error(
"Missing header column '" +
name +
"'");
481 m_tuple_column_map.fill(SIZE_MAX);
484 m_extra_columns.clear();
485 for (std::size_t i = 0; i < m_columns.size(); ++i) {
487 auto it = std::find(names.begin(), names.end(), m_columns[i]);
488 if (
it != names.end()) {
490 m_tuple_column_map[std::distance(names.begin(),
it)] = i;
493 m_extra_columns.push_back(i);