Vince's CSV Parser
Loading...
Searching...
No Matches
csv_row.cpp
Go to the documentation of this file.
1
5#include <cassert>
6#include <functional>
7#include "csv_row.hpp"
8#include "csv_exceptions.hpp"
9
10namespace csv {
11 namespace internals {
12 csv::string_view get_trimmed(csv::string_view sv, const WhitespaceMap& ws_flags) noexcept
13 {
14 // Lazy trim only when requested
15 size_t start = 0;
16 while (start < sv.size() && ws_flags[sv[start] + CHAR_OFFSET]) {
17 ++start;
18 }
19
20 size_t end = sv.size();
21 while (end > start && ws_flags[sv[end - 1] + CHAR_OFFSET]) {
22 --end;
23 }
24
25 return sv.substr(start, end - start);
26 }
27 }
28
29#if CSV_EXCEPTIONS_ENABLED
30 CSVField CSVRow::operator[](size_t n) const {
31 if (n >= this->size()) throw std::runtime_error(internals::CSV_ERROR_INDEX_OUT_OF_BOUNDS);
32 return this->make_field(n, this->data);
33 }
34#endif
35
36#if CSV_EXCEPTIONS_ENABLED
38 auto & col_names = this->data->col_names;
39 auto col_pos = col_names->index_of(col_name);
40 if (col_pos > -1) {
41 return this->operator[](col_pos);
42 }
43
44 internals::throw_column_not_found(col_name);
45 }
46#endif
47 CSVField CSVRow::try_field(size_t index, CSVError& error) const {
48 error.clear();
49 if (index >= size()) {
50 internals::fail_static(error, CSVErrorCode::IndexOutOfBounds, internals::CSV_ERROR_INDEX_OUT_OF_BOUNDS);
51 return CSVField(csv::string_view());
52 }
53 return make_field(index, data);
54 }
55
57 error.clear();
58 const int index = data && data->col_names ? data->col_names->index_of(name) : CSV_NOT_FOUND;
59 if (index == CSV_NOT_FOUND) {
60 internals::fail(error, CSVErrorCode::ColumnNotFound,
61 internals::make_prefixed_message(internals::ERROR_COLUMN_NOT_FOUND, name));
62 return CSVField(csv::string_view());
63 }
64 return try_field(static_cast<size_t>(index), error);
65 }
66
67 CSVRow::operator std::vector<std::string>() const {
68 std::vector<std::string> ret;
69 for (size_t i = 0; i < size(); i++)
70 ret.push_back(std::string(this->get_field(i)));
71
72 return ret;
73 }
74
76 if (!data) return csv::string_view();
77 const csv::string_view full = data->data;
78 if (data_start >= full.size()) return csv::string_view();
79
80 if (data_end != (std::numeric_limits<size_t>::max)()
81 && data_end >= data_start
82 && data_end <= full.size()) {
83 return full.substr(data_start, data_end - data_start);
84 }
85
86 const size_t end = full.find('\n', data_start);
87 const size_t len = (end == csv::string_view::npos)
88 ? (full.size() - data_start)
89 : (end - data_start);
90 return full.substr(data_start, len);
91 }
92
93 std::unordered_map<std::string, std::string> CSVRow::try_to_unordered_map(CSVError& error) const {
94 return to_unordered_map_impl(nullptr, error);
95 }
96
97 std::unordered_map<std::string, std::string> CSVRow::try_to_unordered_map(
98 CSVError& error, const std::vector<std::string>& subset) const {
99 return to_unordered_map_impl(&subset, error);
100 }
101
102 std::unordered_map<std::string, std::string> CSVRow::to_unordered_map_impl(
103 const std::vector<std::string>* subset, CSVError& error) const {
104 error.clear();
105 std::unordered_map<std::string, std::string> out;
106 if (!subset) {
107 if (!data || !data->col_names) return out;
108 const auto& names = get_col_names();
109 if (names.size() < size()) {
110 internals::fail_static(error, CSVErrorCode::IndexOutOfBounds, internals::ERROR_COLUMN_INDEX_OUT_OF_BOUNDS);
111 return out;
112 }
113 out.reserve(size());
114 for (size_t i = 0; i < size(); ++i) out[names[i]] = std::string(get_field(i));
115 } else {
116 out.reserve(subset->size());
117 for (const auto& name : *subset) {
118 auto field = try_field(name, error);
119 if (error) return out;
120 out[name] = std::string(field.get_sv());
121 }
122 }
123 return out;
124 }
125
126#if CSV_EXCEPTIONS_ENABLED
127 std::unordered_map<std::string, std::string> CSVRow::to_unordered_map() const {
128 CSVError error;
129 auto out = try_to_unordered_map(error);
130 if (error) throw std::out_of_range(std::string(error.context));
131 return out;
132 }
133
134 std::unordered_map<std::string, std::string> CSVRow::to_unordered_map(
135 const std::vector<std::string>& subset) const {
136 CSVError error;
137 auto out = try_to_unordered_map(error, subset);
138 internals::throw_error(error);
139 return out;
140 }
141#endif
142
143 csv::string_view CSVRow::get_field(size_t index) const
144 {
145 return this->get_field_impl(index, this->data);
146 }
147
148 csv::string_view CSVRow::get_field_safe(size_t index, internals::RawCSVDataPtr _data) const
149 {
150 return this->get_field_impl(index, _data);
151 }
152
153 CSVField CSVRow::make_field(size_t index, const internals::RawCSVDataPtr& _data) const
154 {
155 const csv::string_view field = this->get_field_impl(index, _data);
156 const size_t field_index = this->fields_start + index;
157 if (_data->has_field_scalars() && field_index < _data->field_scalars.size()) {
158 return CSVField(field, _data->field_scalars[field_index]);
159 }
160
161 return CSVField(field);
162 }
163
164 bool CSVField::try_parse_decimal(long double& dVal, const char decimalSymbol) {
165 // If field has already been parsed to empty, no need to do it aagin:
166 if (this->type_ == DataType::CSV_NULL)
167 return false;
168
169 if (this->type_ == DataType::UNKNOWN)
170 this->get_value();
171
172 if (this->type_ == DataType::CSV_NULL)
173 return false;
174
175 if (this->type_ == DataType::CSV_STRING || this->type_ == DataType::CSV_DOUBLE) {
176 double parsed_value = 0;
177 if (!classify_scalar::parse_float(this->sv.data(), this->sv.data() + this->sv.size(), parsed_value, decimalSymbol)) {
178 if (this->type_ == DataType::CSV_DOUBLE)
179 this->type_ = DataType::CSV_STRING;
180 return false;
181 }
182
183 this->cache_parsed_value(DataType::CSV_DOUBLE, parsed_value);
184 }
185
186 // Integral types are not affected by decimalSymbol and need not be parsed again
187
188 // Either we already had an integral type before, or we we just got any numeric type now.
189 if (this->type_ >= DataType::CSV_INT8 && this->type_ <= DataType::CSV_DOUBLE) {
190 dVal = this->numeric_value_as_long_double();
191 return true;
192 }
193
194 // CSV_NULL or CSV_STRING, not numeric
195 return false;
196 }
197
198 bool CSVField::try_parse_timestamp(std::uint64_t& out) const noexcept {
199 if (this->type_ == DataType::UNKNOWN)
200 this->get_value();
201
202 if (this->type_ == DataType::CSV_TIMESTAMP) {
203 out = this->value_.timestamp;
204 return true;
205 }
206
207 if (this->stores_integral() && this->value_.integer >= 0) {
208 out = static_cast<std::uint64_t>(this->value_.integer);
209 return true;
210 }
211
212 return false;
213 }
214
215#ifdef _MSC_VER
216#pragma region CSVRow Iterator
217#endif
218 CSVRow::iterator CSVRow::begin() const noexcept {
219 return iterator(this, 0);
220 }
221
222 CSVRow::iterator CSVRow::end() const noexcept {
223 return iterator(this, static_cast<iterator::difference_type>(this->size()));
224 }
225
226 CSVRow::reverse_iterator CSVRow::rbegin() const noexcept {
227 return reverse_iterator(this->end());
228 }
229
230 CSVRow::reverse_iterator CSVRow::rend() const noexcept {
231 return reverse_iterator(this->begin());
232 }
233
234 CSVRow::iterator::pointer::pointer(const CSVField& field) noexcept
235 : field_(field) {}
236
237 CSVField* CSVRow::iterator::pointer::operator->() noexcept {
238 return &this->field_;
239 }
240
241 const CSVField* CSVRow::iterator::pointer::operator->() const noexcept {
242 return &this->field_;
243 }
244
245 CSV_NON_NULL(2)
246 CSVRow::iterator::iterator(const CSVRow* row, difference_type index) noexcept
247 : data_(row->data), data_start_(row->data_start),
248 fields_start_(row->fields_start), row_length_(row->row_length), index_(index) {}
249
250 CSVRow::iterator::reference CSVRow::iterator::operator*() const {
251 // Reuse row decoding, trimming and eager-scalar handling with an immutable
252 // span snapshot. Never consult the source CSVRow: readers reuse that object.
253 const CSVRow snapshot(this->data_, this->data_start_, this->fields_start_, this->row_length_);
254 return snapshot.make_field(static_cast<size_t>(this->index_), this->data_);
255 }
256
257 CSVRow::iterator::pointer CSVRow::iterator::operator->() const {
258 return pointer(**this);
259 }
260
261 CSVRow::iterator::reference CSVRow::iterator::operator[](difference_type n) const {
262 return *(*this + n);
263 }
264
265 CSVRow::iterator& CSVRow::iterator::operator++() noexcept {
266 return *this += 1;
267 }
268
269 CSVRow::iterator CSVRow::iterator::operator++(int) noexcept {
270 iterator previous = *this;
271 ++(*this);
272 return previous;
273 }
274
275 CSVRow::iterator& CSVRow::iterator::operator--() noexcept {
276 return *this -= 1;
277 }
278
279 CSVRow::iterator CSVRow::iterator::operator--(int) noexcept {
280 iterator previous = *this;
281 --(*this);
282 return previous;
283 }
284
285 CSVRow::iterator CSVRow::iterator::operator+(difference_type n) const noexcept {
286 iterator result = *this;
287 result += n;
288 return result;
289 }
290
291 CSVRow::iterator& CSVRow::iterator::operator+=(difference_type n) noexcept {
292 this->index_ += n;
293 return *this;
294 }
295
296 CSVRow::iterator CSVRow::iterator::operator-(difference_type n) const noexcept {
297 iterator result = *this;
298 result -= n;
299 return result;
300 }
301
302 CSVRow::iterator& CSVRow::iterator::operator-=(difference_type n) noexcept {
303 this->index_ -= n;
304 return *this;
305 }
306
307 CSVRow::iterator::difference_type CSVRow::iterator::operator-(const iterator& other) const noexcept {
308 return this->index_ - other.index_;
309 }
310
311 CSVRow::iterator operator+(CSVRow::iterator::difference_type n, const CSVRow::iterator& it) noexcept {
312 return it + n;
313 }
314
315 bool CSVRow::iterator::operator==(const iterator& other) const noexcept {
316 return this->data_ == other.data_ && this->data_start_ == other.data_start_
317 && this->fields_start_ == other.fields_start_ && this->row_length_ == other.row_length_
318 && this->index_ == other.index_;
319 }
320
321 bool CSVRow::iterator::operator!=(const iterator& other) const noexcept {
322 return !(*this == other);
323 }
324
325 bool CSVRow::iterator::operator<(const iterator& other) const noexcept {
326 return this->index_ < other.index_;
327 }
328
329 bool CSVRow::iterator::operator>(const iterator& other) const noexcept {
330 return other < *this;
331 }
332
333 bool CSVRow::iterator::operator<=(const iterator& other) const noexcept {
334 return !(other < *this);
335 }
336
337 bool CSVRow::iterator::operator>=(const iterator& other) const noexcept {
338 return !(*this < other);
339 }
340
341 CSVRow::reverse_iterator::reverse_iterator(iterator base) noexcept
342 : base_(std::move(base)) {}
343
344 CSVRow::reverse_iterator::reference CSVRow::reverse_iterator::operator*() const {
345 return this->base_[-1];
346 }
347
348 CSVRow::reverse_iterator::pointer CSVRow::reverse_iterator::operator->() const {
349 return pointer(**this);
350 }
351
352 CSVRow::reverse_iterator::reference CSVRow::reverse_iterator::operator[](difference_type n) const {
353 return *(*this + n);
354 }
355
356 CSVRow::reverse_iterator& CSVRow::reverse_iterator::operator++() noexcept {
357 return *this += 1;
358 }
359
360 CSVRow::reverse_iterator CSVRow::reverse_iterator::operator++(int) noexcept {
361 reverse_iterator previous = *this;
362 ++(*this);
363 return previous;
364 }
365
366 CSVRow::reverse_iterator& CSVRow::reverse_iterator::operator--() noexcept {
367 return *this -= 1;
368 }
369
370 CSVRow::reverse_iterator CSVRow::reverse_iterator::operator--(int) noexcept {
371 reverse_iterator previous = *this;
372 --(*this);
373 return previous;
374 }
375
376 CSVRow::reverse_iterator CSVRow::reverse_iterator::operator+(difference_type n) const noexcept {
377 return reverse_iterator(this->base_ - n);
378 }
379
380 CSVRow::reverse_iterator& CSVRow::reverse_iterator::operator+=(difference_type n) noexcept {
381 this->base_ -= n;
382 return *this;
383 }
384
385 CSVRow::reverse_iterator CSVRow::reverse_iterator::operator-(difference_type n) const noexcept {
386 return reverse_iterator(this->base_ + n);
387 }
388
389 CSVRow::reverse_iterator& CSVRow::reverse_iterator::operator-=(difference_type n) noexcept {
390 this->base_ += n;
391 return *this;
392 }
393
394 CSVRow::reverse_iterator::difference_type
395 CSVRow::reverse_iterator::operator-(const reverse_iterator& other) const noexcept {
396 return other.base_ - this->base_;
397 }
398
399 CSVRow::reverse_iterator operator+(
400 CSVRow::reverse_iterator::difference_type n, const CSVRow::reverse_iterator& it) noexcept {
401 return it + n;
402 }
403
404 bool CSVRow::reverse_iterator::operator==(const reverse_iterator& other) const noexcept {
405 return this->base_ == other.base_;
406 }
407
408 bool CSVRow::reverse_iterator::operator!=(const reverse_iterator& other) const noexcept {
409 return !(*this == other);
410 }
411
412 bool CSVRow::reverse_iterator::operator<(const reverse_iterator& other) const noexcept {
413 return other.base_ < this->base_;
414 }
415
416 bool CSVRow::reverse_iterator::operator>(const reverse_iterator& other) const noexcept {
417 return other < *this;
418 }
419
420 bool CSVRow::reverse_iterator::operator<=(const reverse_iterator& other) const noexcept {
421 return !(other < *this);
422 }
423
424 bool CSVRow::reverse_iterator::operator>=(const reverse_iterator& other) const noexcept {
425 return !(*this < other);
426 }
427
429 return this->base_;
430 }
431#ifdef _MSC_VER
432#pragma endregion CSVRow Iterator
433#endif
434}
Data type representing individual CSV values.
Definition csv_row.hpp:74
bool try_parse_decimal(long double &dVal, const char decimalSymbol='.')
Attempts to parse a decimal (or integer) value using the given symbol, returning true if the value is...
Definition csv_row.cpp:164
bool try_parse_timestamp(std::uint64_t &out) const noexcept
Parse this field as Unix milliseconds.
Definition csv_row.cpp:198
Iterator producing short-lived CSVField values with independent lazy conversion caches.
Definition csv_row.hpp:682
iterator base() const noexcept
The forward iterator one past the field this iterator produces.
Definition csv_row.cpp:428
CSVField try_field(size_t index, CSVError &error) const
Checked field retrieval.
Definition csv_row.cpp:47
csv::string_view raw_str() const noexcept
Return a string_view of the raw bytes of this row as they appear in the underlying parse buffer,...
Definition csv_row.cpp:75
const std::vector< std::string > & get_col_names() const
Retrieve this row's associated column names.
Definition csv_row.hpp:619
std::unordered_map< std::string, std::string > try_to_unordered_map(CSVError &error) const
Materialize named values; unknown names or absent header positions report an error.
Definition csv_row.cpp:93
std::unordered_map< std::string, std::string > to_unordered_map() const
Convert this CSVRow into an unordered map.
Definition csv_row.cpp:127
CONSTEXPR size_t size() const noexcept
Return the number of fields in this row.
Definition csv_row.hpp:555
CSVField operator[](size_t n) const
Return the field at n.
Definition csv_row.cpp:30
Shared exception message templates and throw helpers.
Defines the data type used for storing information about a CSV row.
The all encompassing namespace.
@ CSV_TIMESTAMP
Timestamp value.
@ CSV_DOUBLE
Floating point value.
@ CSV_NULL
Empty string.
@ CSV_INT8
8-bit integer
@ CSV_STRING
Non-scalar string.
@ IndexOutOfBounds
A row/column position exceeds the visible schema or physical record width.
@ ColumnNotFound
A requested name is absent from the visible column schema.
constexpr int CSV_NOT_FOUND
Integer indicating a requested column wasn't found.
Definition common.hpp:494
constexpr unsigned CHAR_OFFSET
Offset to convert char into array index.
Definition common.hpp:497
std::string_view string_view
The string_view class used by this library.
Definition common.hpp:182
A diagnostic whose text remains valid for the lifetime of the error.
Definition csv_error.hpp:94