guanaqo develop
Utilities for scientific software
Loading...
Searching...
No Matches
csv.tpp
Go to the documentation of this file.
1#include <guanaqo/io/csv.hpp>
2
3#include <algorithm>
4#include <array>
5#include <cassert>
6#include <charconv>
7#include <ios>
8#include <iostream>
9
10#if !__cpp_lib_to_chars
11#include <cerrno>
12#include <cstdlib> // strtod
13#endif
14
15namespace guanaqo::io {
16
17template <class F>
18 requires(std::floating_point<F> || std::integral<F>)
19struct CSVReader {
20 static constexpr std::streamsize bufmaxsize = 64;
21 std::array<char, bufmaxsize + 1> s;
22 std::streamsize bufidx = 0;
23 bool keep_reading = true;
24 static constexpr char end = '\n';
25
26 [[nodiscard]] F read(std::istream &is, char sep) {
27 // Get some characters to process
28 if (keep_reading)
29 read_chunk(is);
30 // Parse a number
31 F v;
32 char *bufend = s.data() + bufidx;
33 const char *ptr = read_single(s.data(), bufend, v);
34 // Check separator
35 if (ptr != bufend && *ptr != sep)
36 throw csv_read_error("io::csv_read_row unexpected character '" +
37 std::string{*ptr} + "'");
38 // Shift the buffer over
39 if (ptr != bufend) {
40 std::copy(ptr + 1, static_cast<const char *>(bufend), s.data());
41 bufidx -= ptr + 1 - s.data();
42 } else {
43 bufidx = 0;
44 }
45 return v;
46 }
47
48 void read_chunk(std::istream &is) {
49 using namespace std::string_literals;
50 if (!is)
51 throw csv_read_error("io::csv_read_row invalid stream:"s +
52 (is.bad() ? " bad" : "") + //
53 (is.fail() ? " fail" : "") + //
54 (is.eof() ? " eof" : ""));
55 if (bufmaxsize == bufidx)
56 return;
57 if (!is.get(s.data() + bufidx, bufmaxsize - bufidx + 1, end))
58 throw csv_read_error("io::csv_read_row extraction failed:"s +
59 (is.bad() ? " bad" : "") + //
60 (is.fail() ? " fail" : "") + //
61 (is.eof() ? " eof" : ""));
62 bufidx += is.gcount();
63 keep_reading = is.peek() != end && !is.eof();
64 assert(bufidx <= bufmaxsize);
65 }
66
67 void skip_comments(std::istream &is) {
68 assert(bufidx == 0);
69 if (is.eof() || is.peek() == end)
70 return;
71 while (!is.eof()) {
72 read_chunk(is);
73 if (bufidx == 0 || s.front() != '#')
74 break;
75 while (keep_reading) {
76 bufidx = 0;
77 read_chunk(is);
78 }
79 bufidx = 0;
80 next_line(is);
81 }
82 }
83
84#if __cpp_lib_to_chars
85 static const char *read_single(const char *bufbegin, const char *bufend,
86 F &v) {
87 if (bufbegin != bufend && *bufbegin == '+')
88 ++bufbegin;
89 const auto [ptr, ec] = std::from_chars(bufbegin, bufend, v);
90 const auto bufvw = std::string_view(bufbegin, bufend);
91 if (ec != std::errc{})
92 throw csv_read_error("io::csv_read_row conversion failed '" +
93 std::string(bufvw) +
94 "': " + std::make_error_code(ec).message());
95 return ptr;
96 }
97#else
98 static void strtod_ovl(const char *str, char **str_end, float &v) {
99 v = std::strtof(str, str_end);
100 }
101 static void strtod_ovl(const char *str, char **str_end, double &v) {
102 v = std::strtod(str, str_end);
103 }
104 static void strtod_ovl(const char *str, char **str_end, long double &v) {
105 v = std::strtold(str, str_end);
106 }
107 static void strtod_ovl(const char *str, char **str_end, long long &v) {
108 v = std::strtoll(str, str_end, 10);
109 }
110 static void strtod_ovl(const char *str, char **str_end, long &v) {
111 v = std::strtol(str, str_end, 10);
112 }
113 static void strtod_ovl(const char *str, char **str_end, int &v) {
114 v = static_cast<int>(std::strtol(str, str_end, 10));
115 }
116 static void strtod_ovl(const char *str, char **str_end, short &v) {
117 v = static_cast<short>(std::strtol(str, str_end, 10));
118 }
119 static const char *read_single(const char *bufbegin, char *bufend, F &v) {
120 *bufend = '\0';
121 char *ptr;
122 errno = 0;
123 strtod_ovl(bufbegin, &ptr, v);
124 if (errno || ptr == bufbegin)
125 throw csv_read_error("io::csv_read_row conversion failed '" +
126 std::string(bufbegin) +
127 "': " + std::to_string(errno));
128 return ptr;
129 }
130#endif
131
132 void next_line(std::istream &is) const {
133 if (bufidx > 0 || (!is.eof() && is.get() != end))
134 throw csv_read_error("io::csv_read_row line not fully consumed");
135 }
136
137 [[nodiscard]] bool done(std::istream &is) const {
138 bool keep_reading = is.peek() != end && !is.eof();
139 return bufidx == 0 && !keep_reading;
140 }
141};
142
143#ifdef GUANAQO_WITH_QUAD_PRECISION
144template <>
145inline const char *CSVReader<__float128>::read_single(const char *bufbegin,
146 const char *bufend,
147 __float128 &v) {
148 long double ld;
149 auto ret = CSVReader<long double>::read_single(bufbegin, bufend, ld);
150 v = static_cast<__float128>(ld);
151 return ret;
152}
153#endif
154
155template <class F>
156 requires(std::floating_point<F> || std::integral<F>)
157void csv_read_row(std::istream &is, std::span<F> v, char sep) {
158 CSVReader<F> reader;
159 reader.skip_comments(is);
160 for (auto &vv : v)
161 vv = reader.read(is, sep);
162 reader.next_line(is);
163}
164
165template <class F>
166 requires(std::floating_point<F> || std::integral<F>)
168 char sep) {
169 CSVReader<F> reader;
170 for (ptrdiff_t r = 0; r < v.rows; ++r) {
171 reader.skip_comments(is);
172 for (ptrdiff_t c = 0; c < v.cols; ++c)
173 v(r, c) = reader.read(is, sep);
174 reader.next_line(is);
175 }
176}
177
178template <class F>
179 requires(std::floating_point<F> || std::integral<F>)
180std::vector<F> csv_read_row_std_vector(std::istream &is, char sep) {
181 CSVReader<F> reader;
182 std::vector<F> v;
183 reader.skip_comments(is);
184 while (!reader.done(is))
185 v.push_back(reader.read(is, sep));
186 reader.next_line(is);
187 return v;
188}
189
190} // namespace guanaqo::io
Reading matrices and vectors from CSV files.
void csv_read_row(std::istream &is, std::span< F > v, char sep)
Definition csv.tpp:157
std::vector< F > csv_read_row_std_vector(std::istream &is, char sep)
Definition csv.tpp:180
void csv_read(std::istream &is, MatrixView< F, ptrdiff_t, ptrdiff_t > v, char sep)
Definition csv.tpp:167
A lightweight view of a 2D matrix.
Definition mat-view.hpp:68
static void strtod_ovl(const char *str, char **str_end, int &v)
Definition csv.tpp:113
void skip_comments(std::istream &is)
Definition csv.tpp:67
static const char * read_single(const char *bufbegin, char *bufend, F &v)
Definition csv.tpp:119
static constexpr char end
Definition csv.tpp:24
static void strtod_ovl(const char *str, char **str_end, long long &v)
Definition csv.tpp:107
static void strtod_ovl(const char *str, char **str_end, long &v)
Definition csv.tpp:110
static void strtod_ovl(const char *str, char **str_end, short &v)
Definition csv.tpp:116
static void strtod_ovl(const char *str, char **str_end, float &v)
Definition csv.tpp:98
std::streamsize bufidx
Definition csv.tpp:22
void read_chunk(std::istream &is)
Definition csv.tpp:48
static void strtod_ovl(const char *str, char **str_end, double &v)
Definition csv.tpp:101
F read(std::istream &is, char sep)
Definition csv.tpp:26
bool done(std::istream &is) const
Definition csv.tpp:137
void next_line(std::istream &is) const
Definition csv.tpp:132
static constexpr std::streamsize bufmaxsize
Definition csv.tpp:20
std::array< char, bufmaxsize+1 > s
Definition csv.tpp:21
static void strtod_ovl(const char *str, char **str_end, long double &v)
Definition csv.tpp:104