Vince's CSV Parser
Loading...
Searching...
No Matches
csv_reader.hpp
Go to the documentation of this file.
1
5#pragma once
6
7#include <algorithm>
8#include <deque>
9#include <exception>
10#include <fstream>
11#include <functional>
12#include <iterator>
13#include <memory>
14#include <sstream>
15#include <string>
16#include <vector>
17
18#include "common.hpp"
19#include "csv_exceptions.hpp"
20#include "data_type.hpp"
21#include "csv_format.hpp"
22#include "parser/mmap.hpp"
23#include "parser/scheduler.hpp"
24#include "parser/stream.hpp"
25
27namespace csv {
49 class CSVReader {
50 public:
67#if CSV_EXCEPTIONS_ENABLED
68 class iterator {
69 public:
70 #ifndef DOXYGEN_SHOULD_SKIP_THIS
71 using value_type = CSVRow;
72 using difference_type = std::ptrdiff_t;
73 using pointer = CSVRow * ;
74 using reference = CSVRow & ;
75 using iterator_category = std::input_iterator_tag;
76 #endif
77
78 iterator() = default;
79 iterator(CSVReader* reader) : daddy(reader) {}
81
83 CONSTEXPR_14 reference operator*() { return this->row; }
84 CONSTEXPR_14 reference operator*() const { return const_cast<reference>(this->row); }
85
87 CONSTEXPR_14 pointer operator->() { return &(this->row); }
88 CONSTEXPR_14 pointer operator->() const { return const_cast<pointer>(&(this->row)); }
89
90 iterator& operator++();
91 iterator operator++(int);
96 CONSTEXPR bool operator==(const iterator& other) const noexcept {
97 return (this->daddy == other.daddy) && (this->i == other.i);
98 }
99
100 CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); }
101 private:
102 CSVReader * daddy = nullptr; // Pointer to parent
103 CSVRow row; // Current row
104 size_t i = 0; // Index of current row
105 };
106#endif
107
112
128 static std::unique_ptr<CSVReader> try_open(
129 csv::string_view filename, CSVError& error,
130 const CSVFormat& format = CSVFormat::guess_csv());
131
132 template<typename TStream,
133 csv::enable_if_t<std::is_base_of<std::istream, TStream>::value, int> = 0>
134 static std::unique_ptr<CSVReader> try_open(
135 TStream& source, CSVError& error,
136 const CSVFormat& format = CSVFormat::guess_csv()) {
137 error.clear();
138 std::unique_ptr<CSVReader> reader(new CSVReader(format, initialize_tag{}));
139 reader->init_from_stream(source, format);
140 error = reader->error_;
141 return error ? nullptr : std::move(reader);
142 }
143
144 static std::unique_ptr<CSVReader> try_open(
145 std::unique_ptr<std::istream> source, CSVError& error,
146 const CSVFormat& format = CSVFormat::guess_csv());
147
148#ifdef CSV_HAS_STD_EXPECTED
149 template<typename Source>
150 static std::expected<std::unique_ptr<CSVReader>, CSVError> try_open(
151 Source&& source, const CSVFormat& format = CSVFormat::guess_csv()) {
152 CSVError error;
153 auto reader = try_open(std::forward<Source>(source), error, format);
154 if (error) return std::unexpected(std::move(error));
155 return reader;
156 }
157#endif
158
159#if CSV_EXCEPTIONS_ENABLED
160 CSVReader(csv::string_view filename, const CSVFormat& format = CSVFormat::guess_csv())
161 : CSVReader(format, initialize_tag{}) {
162 open_file(filename, format);
163 internals::throw_error(error_);
164 }
165
178 template<typename TStream,
179 csv::enable_if_t<std::is_base_of<std::istream, TStream>::value, int> = 0>
180 CSVReader(TStream& source, const CSVFormat& format = CSVFormat::guess_csv())
181 : CSVReader(format, initialize_tag{}) {
182 init_from_stream(source, format);
183 internals::throw_error(error_);
184 }
185
191 CSVReader(std::unique_ptr<std::istream> source,
192 const CSVFormat& format = CSVFormat::guess_csv())
193 : CSVReader(format, initialize_tag{}) {
194 owned_stream = std::move(source);
195 if (!owned_stream)
196 throw std::invalid_argument(internals::ERROR_READER_NULL_STREAM);
197 init_from_stream(*owned_stream, format);
198 internals::throw_error(error_);
199 }
200#endif
202
203 CSVReader(const CSVReader&) = delete;
204 CSVReader& operator=(const CSVReader&) = delete;
205
214 CSVReader(CSVReader&& other) noexcept :
215 read_scheduler_(other._format.is_threading_enabled()) {
216 other.read_scheduler_.join();
217 this->move_state_from(other);
218 }
219
224 CSVReader& operator=(CSVReader&& other) noexcept {
225 if (this == &other) {
226 return *this;
227 }
228
229 this->read_scheduler_.join();
230 other.read_scheduler_.join();
231 this->move_state_from(other);
232
233 return *this;
234 }
235
236 ~CSVReader() {
237 this->read_scheduler_.join();
238 }
239
242
260#if CSV_EXCEPTIONS_ENABLED
261 bool read_row(CSVRow &row);
262#endif
266 bool try_read_row(CSVRow& row, CSVError& error);
267
289#if CSV_EXCEPTIONS_ENABLED
290 bool read_chunk(std::vector<CSVRow>& out, size_t max_rows);
291 iterator begin();
292 CSV_CONST iterator end() const noexcept;
293#endif
297 bool try_read_chunk(std::vector<CSVRow>& out, size_t max_rows, CSVError& error);
298
300 bool eof() const noexcept { return this->parser->eof(); }
302
305
310 CSVFormat get_format() const;
311
313 const std::vector<std::string>& get_col_names() const{
314 static const std::vector<std::string> empty_col_names;
315 return (this->col_names) ? this->col_names->get_col_names() : empty_col_names;
316 }
317
319 internals::ConstColNamesPtr col_names_ptr() const noexcept {
320 return this->col_names;
321 }
322
324 int index_of(csv::string_view col_name) const {
325 return this->col_names->index_of(col_name);
326 }
328
331
337 CONSTEXPR bool empty() const noexcept { return this->n_rows() == 0; }
338
340 CONSTEXPR size_t n_rows() const noexcept { return this->_n_rows; }
341
343 bool utf8_bom() const noexcept { return this->parser->utf8_bom(); }
344
346 internals::SpeculativeParseDiagnostics speculative_diagnostics() const noexcept {
347 return this->parser ? this->parser->speculative_diagnostics()
348 : internals::SpeculativeParseDiagnostics();
349 }
350
352 size_t parse_worker_count() const noexcept {
353 return this->parser ? this->parser->parse_worker_count() : 1;
354 }
356
357 protected:
366 void set_col_names(const std::vector<std::string>&);
367
370 CSVFormat _format;
372
375
376 internals::ColNamesPtr col_names = std::make_shared<internals::ColNames>();
377
379 std::unique_ptr<internals::parser::CSVParserDriverBase> parser = nullptr;
380
382 std::unique_ptr<RowCollection> records{new RowCollection(100)};
383
389 std::unique_ptr<std::istream> owned_stream = nullptr;
390
391 size_t n_cols = 0;
392 size_t _n_rows = 0;
399 bool read_csv(size_t bytes = internals::CSV_CHUNK_SIZE_DEFAULT);
401
404 private:
405 struct initialize_tag {};
406 CSVReader(const CSVFormat& format, initialize_tag)
407 : _format(format), read_scheduler_(format.is_threading_enabled()) {}
408 CSVError error_;
409 void open_file(csv::string_view filename, const CSVFormat& format);
410
412 bool header_trimmed = false;
413
415 int header_rows_trimmed_ = 0;
416
419 size_t _chunk_size = internals::CSV_CHUNK_SIZE_DEFAULT;
420 bool _read_requested = false;
421 internals::CSVReadScheduler read_scheduler_;
423
424 void move_state_from(CSVReader& other) noexcept {
425 this->_format = std::move(other._format);
426 this->error_ = std::move(other.error_);
427 this->col_names = std::move(other.col_names);
428 this->parser = std::move(other.parser);
429 this->records = std::move(other.records);
430 this->owned_stream = std::move(other.owned_stream);
431 this->n_cols = other.n_cols;
432 this->_n_rows = other._n_rows;
433 this->header_trimmed = other.header_trimmed;
434 this->header_rows_trimmed_ = other.header_rows_trimmed_;
435 this->_chunk_size = other._chunk_size;
436 this->_read_requested = other._read_requested;
437 this->read_scheduler_.set_threading_enabled(this->_format.is_threading_enabled());
438 this->read_scheduler_.adopt_exception(other.read_scheduler_.take_exception());
439 other.reset_after_move();
440 }
441
442 void reset_after_move() noexcept {
443 this->n_cols = 0;
444 this->_n_rows = 0;
445 this->header_trimmed = false;
446 this->header_rows_trimmed_ = 0;
447 this->_read_requested = false;
448 this->_chunk_size = internals::CSV_CHUNK_SIZE_DEFAULT;
449 }
450
456
459 void init_parser(std::unique_ptr<internals::parser::CSVParserDriverBase> parser);
460
461 template<typename TStream,
462 csv::enable_if_t<std::is_base_of<std::istream, TStream>::value, int> = 0>
463 void init_from_stream(TStream& source, const CSVFormat& format) {
464 this->init_parser(
465 std::unique_ptr<internals::parser::CSVParserDriverBase>(
466 new internals::parser::StreamParser<TStream>(source, format, this->col_names)
467 )
468 );
469 }
470
472 void initial_read();
473
474 void trim_header();
476
482
483 bool accept_row(CSVRow&& candidate, CSVRow* single_row, std::vector<CSVRow>* batch_rows);
484
491 bool check_for_rows();
492
498 void drain_rows_into_chunk(std::vector<CSVRow>& out, size_t max_rows);
500 };
501
502 #if defined(CSV_HAS_CXX20) && CSV_EXCEPTIONS_ENABLED
503 static_assert(
504 internals::csv_write_rows_input_range<CSVReader>,
505 "CSVReader must remain compatible with csv::DelimWriter::write_rows()."
506 );
507 #endif
508}
Stores information about how to parse a CSV file.
static CSVFormat guess_csv()
CSVFormat preset for delimiter inference with header/n_cols inference enabled.
An input iterator capable of handling large files.
CONSTEXPR bool operator==(const iterator &other) const noexcept
Returns true if iterators were constructed from the same CSVReader and point to the same row.
iterator & operator++()
Pre-increment iterator.
CONSTEXPR_14 reference operator*()
Access the CSVRow held by the iterator.
CONSTEXPR_14 pointer operator->()
Return a pointer to the CSVRow the iterator has stopped at.
Main class for parsing CSVs from files and in-memory sources.
CONSTEXPR bool empty() const noexcept
Whether or not the file or stream contains valid CSV rows, not including the header.
bool read_chunk(std::vector< CSVRow > &out, size_t max_rows)
Read up to max_rows rows into a caller-owned batch buffer.
bool utf8_bom() const noexcept
Whether or not CSV was prefixed with a UTF-8 bom.
bool try_read_chunk(std::vector< CSVRow > &out, size_t max_rows, CSVError &error)
Clear out and read a batch.
internals::SpeculativeParseDiagnostics speculative_diagnostics() const noexcept
Return speculative-parsing counters for filename-backed readers.
CSVFormat get_format() const
Return the resolved parsing format for this CSV source.
internals::ConstColNamesPtr col_names_ptr() const noexcept
Internal accessor for preserving resolved column-name lookup policy across helper types.
int index_of(csv::string_view col_name) const
Return the index of col_name, or csv::CSV_NOT_FOUND if absent.
size_t parse_worker_count() const noexcept
Return the number of parser worker threads used by the active parser.
const std::vector< std::string > & get_col_names() const
Return the active column names in CSV order.
CSVReader & operator=(const CSVReader &)=delete
Not copyable.
CSV_CONST iterator end() const noexcept
A placeholder for the imaginary past-the-end row in a CSV.
CONSTEXPR size_t n_rows() const noexcept
Retrieves the number of rows that have been read so far.
CSVReader(std::unique_ptr< std::istream > source, const CSVFormat &format=CSVFormat::guess_csv())
Construct CSVReader from an owned std::istream.
bool eof() const noexcept
Returns true if we have reached end of file.
CSVReader & operator=(CSVReader &&other) noexcept
Move assignment.
static std::unique_ptr< CSVReader > try_open(csv::string_view filename, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Construct CSVReader from filename.
CSVReader(CSVReader &&other) noexcept
Move constructor.
bool read_row(CSVRow &row)
Retrieve the next CSV row, returning true while more rows are available.
CSVReader(TStream &source, const CSVFormat &format=CSVFormat::guess_csv())
Construct CSVReader from std::istream.
bool try_read_row(CSVRow &row, CSVError &error)
Retrieve a row, distinguishing EOF (false, no error) from failure.
CSVReader(const CSVReader &)=delete
Not copyable.
iterator begin()
Return an iterator to the first row in the reader.
Data structure for representing CSV rows.
Definition csv_row.hpp:533
A standalone header file containing shared code.
#define CONSTEXPR
Expands to constexpr in decent compilers and inline otherwise.
Definition common.hpp:259
Shared exception message templates and throw helpers.
Defines an object used to store CSV format settings.
CSV scalar type classification adapter.
std::unique_ptr< std::istream > owned_stream
Optional owned stream used by two paths: 1) Emscripten filename-constructor fallback to stream parsin...
std::unique_ptr< RowCollection > records
Queue of parsed CSV rows.
size_t _n_rows
How many rows (minus header) have been read so far.
bool read_csv(size_t bytes=internals::CSV_CHUNK_SIZE_DEFAULT)
Read a chunk of CSV data.
internals::ColNamesPtr col_names
Pointer to a object containing column information.
void set_col_names(const std::vector< std::string > &)
Sets this reader's column names and associated data.
std::unique_ptr< internals::parser::CSVParserDriverBase > parser
Helper class which actually does the parsing.
size_t n_cols
The number of columns in this CSV.
The all encompassing namespace.
std::string_view string_view
The string_view class used by this library.
Definition common.hpp:182