Vince's CSV Parser
Loading...
Searching...
No Matches
csv_utility.hpp
1#pragma once
2#include "common.hpp"
3#include "csv_exceptions.hpp"
4#include "csv_format.hpp"
5#include "csv_reader.hpp"
6#include "data_frame.hpp"
7#include "data_type.hpp"
8#include "string_view_stream.hpp"
9
10#include <memory>
11#include <sstream>
12#include <string>
13#include <type_traits>
14#include <unordered_map>
15#include <utility>
16
17namespace csv {
19 struct CSVFileInfo {
20 std::string filename;
21 std::vector<std::string> col_names;
22 char delim;
23 size_t n_rows;
24 size_t n_cols;
26 internals::SpeculativeParseDiagnostics speculative_diagnostics;
27 };
28
33
42 inline std::unique_ptr<CSVReader> try_parse(
43 csv::string_view in, CSVError& error, const CSVFormat& format = CSVFormat::guess_csv()
44 ) {
45 std::unique_ptr<std::istream> stream(new std::stringstream(std::string(in)));
46 return CSVReader::try_open(std::move(stream), error, format);
47 }
48
49#if CSV_EXCEPTIONS_ENABLED
50 inline CSVReader parse(csv::string_view in, const CSVFormat& format = CSVFormat::guess_csv()) {
51 CSVError error;
52 auto reader = try_parse(in, error, format);
53 internals::throw_error(error);
54 return std::move(*reader);
55 }
56#endif
57
67 inline std::unique_ptr<CSVReader> try_parse_unsafe(
68 csv::string_view in, CSVError& error, const CSVFormat& format = CSVFormat::guess_csv()
69 ) {
70 std::unique_ptr<std::istream> stream(new internals::StringViewStream(in));
71 return CSVReader::try_open(std::move(stream), error, format);
72 }
73
74#if CSV_EXCEPTIONS_ENABLED
75 inline CSVReader parse_unsafe(csv::string_view in, const CSVFormat& format = CSVFormat::guess_csv()) {
76 CSVError error;
77 auto reader = try_parse_unsafe(in, error, format);
78 internals::throw_error(error);
79 return std::move(*reader);
80 }
81#endif
82
83#if CSV_EXCEPTIONS_ENABLED
86 CSVFormat format;
87 format.header_row(-1);
88 return parse(in, format);
89 }
90
99 inline CSVReader operator ""_csv(const char* in, size_t n) {
100 return parse_unsafe(csv::string_view(in, n));
101 }
102
108 inline CSVReader operator ""_csv_no_header(const char* in, size_t n) {
109 CSVFormat format;
110 format.header_row(-1);
111 return parse_unsafe(csv::string_view(in, n), format);
112 }
113#endif
115
118
123 std::unordered_map<std::string, DataType> try_csv_data_types(CSVReader& reader, CSVError& error);
124
126 template<typename Source>
128 Source&& source, CSVError& error, const CSVFormat& format = CSVFormat::guess_csv()
129 ) -> decltype(CSVReader::try_open(std::forward<Source>(source), error, format),
130 std::unordered_map<std::string, DataType>()) {
131 auto reader = CSVReader::try_open(std::forward<Source>(source), error, format);
132 return reader ? try_csv_data_types(*reader, error) : std::unordered_map<std::string, DataType>();
133 }
134
135#if CSV_EXCEPTIONS_ENABLED
136 std::unordered_map<std::string, DataType> csv_data_types(CSVReader& reader);
137
154 template<
155 typename... ReaderArgs,
156 csv::enable_if_t<std::is_constructible<CSVReader, ReaderArgs...>::value, int> = 0
157 >
158 inline std::unordered_map<std::string, DataType> csv_data_types(ReaderArgs&&... reader_args) {
159 CSVReader reader(std::forward<ReaderArgs>(reader_args)...);
160 return csv_data_types(reader);
161 }
162#endif
163
164 namespace internals {
165 // Shared batch traversal lets callers inspect joined task errors before
166 // consuming another batch, including when the source never reaches EOF.
167 template<typename Fn>
168 inline bool try_for_each_csv_chunk(
169 CSVReader& reader, Fn&& fn, CSVError& error, size_t chunk_size) {
170 error.clear();
171 if (chunk_size == 0)
172 return fail_static(error, CSVErrorCode::InvalidArgument, ERROR_CHUNK_PARALLEL_APPLY_ZERO);
173 std::vector<CSVRow> rows;
174 while (reader.try_read_chunk(rows, chunk_size, error)) {
175 DataFrame<> batch(std::move(rows));
176 if (!fn(batch, error)) return false;
177 }
178 return !error;
179 }
180 }
181
194 template<typename State, typename Fn>
196 CSVReader& reader,
197 DataFrameExecutor& executor,
198 std::vector<State>& states,
199 Fn&& fn,
200 CSVError& error,
201 size_t chunk_size = 50000
202 ) {
203 return internals::try_for_each_csv_chunk(reader,
204 [&executor, &states, &fn](DataFrame<>& batch, CSVError& failure) {
205 return batch.try_column_parallel_apply(executor, states, std::forward<Fn>(fn), failure);
206 }, error, chunk_size);
207 }
208
209#if CSV_EXCEPTIONS_ENABLED
210 template<typename State, typename Fn>
211 inline void chunk_parallel_apply(
212 CSVReader& reader,
213 DataFrameExecutor& executor,
214 std::vector<State>& states,
215 Fn&& fn,
216 size_t chunk_size = 50000
217 ) {
218 CSVError error;
219 try_chunk_parallel_apply(reader, executor, states, std::forward<Fn>(fn), error, chunk_size);
220 internals::throw_dataframe_error(error);
221 }
222#endif
223
229 template<typename State, typename Fn>
231 CSVReader& reader,
232 std::vector<State>& states,
233 Fn&& fn,
234 CSVError& error,
235 size_t chunk_size = 50000
236 ) {
237 DataFrameExecutor executor;
238 return try_chunk_parallel_apply(reader, executor, states, std::forward<Fn>(fn), error, chunk_size);
239 }
240
241#if CSV_EXCEPTIONS_ENABLED
242 template<typename State, typename Fn>
243 inline void chunk_parallel_apply(
244 CSVReader& reader,
245 std::vector<State>& states,
246 Fn&& fn,
247 size_t chunk_size = 50000
248 ) {
249 CSVError error;
250 try_chunk_parallel_apply(reader, states, std::forward<Fn>(fn), error, chunk_size);
251 internals::throw_dataframe_error(error);
252 }
253#endif
254
258 inline CSVFileInfo try_get_file_info(const std::string& filename, CSVError& error) {
259 auto reader = CSVReader::try_open(filename, error);
260 if (!reader) return {};
261
262 CSVFormat format = reader->get_format();
263 const char delimiter = internals::resolved_delimiter(format);
264
265 std::vector<CSVRow> rows;
266 while (reader->try_read_chunk(rows, 50000, error)) {}
267 if (error) return {};
268
269 return {
270 filename,
271 reader->get_col_names(),
272 delimiter,
273 reader->n_rows(),
274 reader->get_col_names().size(),
275 reader->parse_worker_count(),
276 reader->speculative_diagnostics()
277 };
278 }
279
280#if CSV_EXCEPTIONS_ENABLED
281 inline CSVFileInfo get_file_info(const std::string& filename) {
282 CSVError error;
283 auto result = try_get_file_info(filename, error);
284 internals::throw_error(error);
285 return result;
286 }
287#endif
288
290 inline std::vector<std::string> try_get_col_names(
291 csv::string_view filename, CSVError& error,
292 const CSVFormat& format = CSVFormat::guess_csv()
293 ) {
294 auto head = internals::parser::get_csv_head(filename, error);
295 if (error) return {};
296 auto reader = try_parse_unsafe(head, error, format);
297 return reader ? reader->get_col_names() : std::vector<std::string>();
298 }
299
303 inline long long try_get_col_pos(
304 csv::string_view filename, csv::string_view col_name, CSVError& error,
305 const CSVFormat& format = CSVFormat::guess_csv()
306 ) {
307 auto col_names = try_get_col_names(filename, error, format);
308 if (error) return CSV_NOT_FOUND;
309 const auto position = std::find(col_names.begin(), col_names.end(), col_name);
310 return position == col_names.end() ? CSV_NOT_FOUND
311 : static_cast<long long>(std::distance(col_names.begin(), position));
312 }
313
314#if CSV_EXCEPTIONS_ENABLED
316 inline std::vector<std::string> get_col_names(
317 csv::string_view filename,
318 const CSVFormat& format = CSVFormat::guess_csv()
319 ) {
320 CSVError error;
321 auto result = try_get_col_names(filename, error, format);
322 internals::throw_error(error);
323 return result;
324 }
325
327 inline long long get_col_pos(
328 csv::string_view filename, csv::string_view col_name,
329 const CSVFormat& format = CSVFormat::guess_csv()
330 ) {
331 CSVError error;
332 const auto result = try_get_col_pos(filename, col_name, error, format);
333 internals::throw_error(error);
334 return result;
335 }
336#endif
338}
Stores information about how to parse a CSV file.
static CSVFormat guess_csv()
CSVFormat preset for delimiter inference with header/n_cols inference enabled.
CSVFormat & header_row(int row)
Sets the header row.
Main class for parsing CSVs from files and in-memory sources.
static std::unique_ptr< CSVReader > try_open(csv::string_view filename, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Construct CSVReader from filename.
Lightweight istream over csv::string_view with zero copy.
A standalone header file containing shared code.
Shared exception message templates and throw helpers.
Defines an object used to store CSV format settings.
Defines functionality needed for basic CSV parsing.
CSV scalar type classification adapter.
The all encompassing namespace.
bool try_chunk_parallel_apply(CSVReader &reader, DataFrameExecutor &executor, std::vector< State > &states, Fn &&fn, CSVError &error, size_t chunk_size=50000)
Apply a per-column batch function over a CSVReader using a reusable executor.
CSVFileInfo try_get_file_info(const std::string &filename, CSVError &error)
Get basic information about a CSV file.
long long get_col_pos(csv::string_view filename, csv::string_view col_name, const CSVFormat &format=CSVFormat::guess_csv())
Find the position of a column in a CSV file or CSV_NOT_FOUND otherwise.
@ InvalidArgument
Invalid operation input, such as a null stream or zero batch size.
std::unordered_map< std::string, DataType > try_csv_data_types(CSVReader &reader, CSVError &error)
Infer SQL-friendly column data types from an existing CSVReader.
std::vector< std::string > try_get_col_names(csv::string_view filename, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Get the column names of a CSV file using just the first 500KB.
std::vector< std::string > get_col_names(csv::string_view filename, const CSVFormat &format=CSVFormat::guess_csv())
Get the column names of a CSV file using just the first 500KB.
CSVReader parse_no_header(csv::string_view in)
Parses a CSV string with no headers.
constexpr int CSV_NOT_FOUND
Integer indicating a requested column wasn't found.
Definition common.hpp:494
long long try_get_col_pos(csv::string_view filename, csv::string_view col_name, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Find a column position, returning CSV_NOT_FOUND for an absent name or on failure.
std::unique_ptr< CSVReader > try_parse(csv::string_view in, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Parse CSV from a string view, copying the input into an owned buffer.
std::unique_ptr< CSVReader > try_parse_unsafe(csv::string_view in, CSVError &error, const CSVFormat &format=CSVFormat::guess_csv())
Parse CSV from an in-memory view with zero copy.
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
Returned by the file inspection utilities.
size_t n_cols
Number of columns in a CSV.
std::vector< std::string > col_names
CSV column names.
size_t parse_worker_count
Number of parser worker threads used.
char delim
Delimiting character.
std::string filename
Filename.
size_t n_rows
Number of rows in a file.