8 void CSVReader::open_file(
csv::string_view filename,
const CSVFormat& format) {
9#if defined(__EMSCRIPTEN__)
10 owned_stream.reset(
new std::ifstream(std::string(filename), std::ios::binary));
13 internals::make_prefixed_message(internals::ERROR_CANNOT_OPEN_FILE, filename));
18 init_parser(std::unique_ptr<internals::parser::CSVParserDriverBase>(
19 new internals::parser::MmapParser(filename, format,
col_names)));
26 std::unique_ptr<CSVReader> reader(
new CSVReader(format, initialize_tag{}));
27 reader->open_file(filename, format);
28 error = reader->error_;
29 return error ? nullptr : std::move(reader);
39 std::unique_ptr<CSVReader> reader(
new CSVReader(format, initialize_tag{}));
40 reader->owned_stream = std::move(source);
41 reader->init_from_stream(*reader->owned_stream, format);
42 error = reader->error_;
43 return error ? nullptr : std::move(reader);
47#pragma region Reading helpers
49 bool CSVReader::check_for_rows() {
50 if (error_)
return false;
51 if (!this->
records->empty())
return true;
53 if (this->read_scheduler_.wait_if_active(
54 [
this] { return this->records->is_waitable(); },
55 [
this] { this->records->wait(); }
60 this->read_scheduler_.join();
61 this->read_scheduler_.rethrow_exception_if_any();
67 if (this->
parser->eof())
return false;
69 if (this->_read_requested && this->
records->empty()) {
71 internals::make_row_larger_than_chunk_message(this->_chunk_size));
74 this->read_scheduler_.run(
75 [
this] { this->
read_csv(this->_chunk_size); },
76 [
this] { this->
records->notify_all(); }
78 this->read_scheduler_.rethrow_exception_if_any();
80 this->_read_requested =
true;
85#pragma endregion Reading helpers
89#pragma region Format and header helpers
91 void CSVReader::init_parser(
92 std::unique_ptr<internals::parser::CSVParserDriverBase> parser_impl
94 if (parser_impl->error()) {
95 error_ = parser_impl->error();
98 auto resolved = parser_impl->get_resolved_format();
99 this->_format = resolved.format;
100 this->_chunk_size = this->_format.get_chunk_size();
101 this->
n_cols = resolved.n_cols;
103 if (!this->_format.col_names.empty()) {
107 this->
parser = std::move(parser_impl);
108 this->initial_read();
117 new_format.col_names = this->
col_names->get_col_names();
118 new_format.header = this->_format.header;
123 void CSVReader::initial_read() {
127 const int trimmed_before = this->header_rows_trimmed_;
128 this->read_scheduler_.run([
this] { this->
read_csv(this->_chunk_size); });
129 this->read_scheduler_.join();
130 this->read_scheduler_.rethrow_exception_if_any();
138 if (!this->header_trimmed && !this->
parser->eof()
139 && this->header_rows_trimmed_ == trimmed_before) {
141 internals::make_row_larger_than_chunk_message(this->_chunk_size));
144 }
while (!this->header_trimmed && !this->
parser->eof());
147 void CSVReader::trim_header() {
148 if (!this->header_trimmed) {
151 while (this->header_rows_trimmed_ <= this->_format.header && !this->records->empty()) {
152 if (this->header_rows_trimmed_ == this->_format.header && this->col_names->empty()) {
159 this->header_rows_trimmed_++;
162 this->header_trimmed = this->header_rows_trimmed_ > this->_format.header;
169 this->
col_names->set_policy(this->_format.get_column_name_policy());
171 this->
n_cols = names.size();
174#pragma endregion Format and header helpers
178#pragma region Reading helpers
180 bool CSVReader::accept_row(
CSVRow&& candidate,
CSVRow* single_row, std::vector<CSVRow>* batch_rows) {
181 const auto policy = this->_format.variable_column_policy;
182 const size_t next_row_size = candidate.size();
184 if (policy == VariableColumnPolicy::KEEP_NON_EMPTY && next_row_size == 0) {
188 if (next_row_size != this->
n_cols &&
189 (policy == VariableColumnPolicy::THROW || policy == VariableColumnPolicy::IGNORE_ROW)) {
190 if (policy == VariableColumnPolicy::THROW) {
191 if (candidate.size() < this->n_cols) {
193 internals::make_prefixed_message(internals::ERROR_ROW_TOO_SHORT, candidate.raw_str()));
197 internals::make_prefixed_message(internals::ERROR_ROW_TOO_LONG, candidate.raw_str()));
203 if (single_row !=
nullptr) {
204 *single_row = std::move(candidate);
205 }
else if (batch_rows !=
nullptr) {
206 batch_rows->push_back(std::move(candidate));
212 this->_read_requested =
false;
216 void CSVReader::drain_rows_into_chunk(std::vector<CSVRow>& out,
size_t max_rows) {
217 std::vector<CSVRow> drained;
218 drained.reserve(max_rows - out.size());
219 this->
records->drain_front(drained, max_rows - out.size());
221 for (
size_t i = 0; i < drained.size(); ++i) {
222 this->accept_row(std::move(drained[i]),
nullptr, &out);
227#pragma endregion Reading helpers
231#pragma region Worker reading methods
263 this->
parser->next(bytes);
265 if (!
parser->error() && !this->header_trimmed) {
282 while (this->check_for_rows()) {
286 if (this->accept_row(this->
records->pop_front(), &row,
nullptr))
302 while (out.size() < max_rows) {
303 if (check_for_rows()) {
308 const size_t before_size = out.size();
309 this->drain_rows_into_chunk(out, max_rows);
310 if (error_) { error = error_;
return false; }
312 if (out.size() == before_size) {
316 else { error = error_;
return !error && !out.empty(); }
321#if CSV_EXCEPTIONS_ENABLED
325 internals::throw_error(error);
330 if (max_rows == 0) { out.clear();
return false; }
333 internals::throw_error(error);
338#pragma endregion Worker reading methods
Main class for parsing CSVs from files and in-memory sources.
bool read_chunk(std::vector< CSVRow > &out, size_t max_rows)
Read up to max_rows rows into a caller-owned batch buffer.
bool try_read_chunk(std::vector< CSVRow > &out, size_t max_rows, CSVError &error)
Clear out and read a batch.
CSVFormat get_format() const
Return the resolved parsing format for this CSV source.
static std::unique_ptr< CSVReader > try_open(csv::string_view filename, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Construct CSVReader from filename.
bool read_row(CSVRow &row)
Retrieve the next CSV row, returning true while more rows are available.
bool try_read_row(CSVRow &row, CSVError &error)
Retrieve a row, distinguishing EOF (false, no error) from failure.
Data structure for representing CSV rows.
Defines functionality needed for basic CSV parsing.
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.
@ InvalidArgument
Invalid operation input, such as a null stream or zero batch size.
@ RowTooLarge
A complete record could not fit within the configured read window.
@ RowLengthMismatch
A record's field count violates the configured variable-column policy.
@ OpenFailed
A native file could not be opened, including initial input mapping.
std::string_view string_view
The string_view class used by this library.
A diagnostic whose text remains valid for the lifetime of the error.