csvpp 1.2.0
CSV parsing / writing libraries
Loading...
Searching...
No Matches
csv.hpp
Go to the documentation of this file.
1
3
4// Copyright 2020 Matthew Chandler
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22// SOFTWARE.
23
24#ifndef CSV_HPP
25#define CSV_HPP
26
27#include <exception>
28#include <fstream>
29#include <map>
30#include <memory>
31#include <optional>
32#include <sstream>
33#include <string>
34#include <vector>
35
36#include <cassert>
37#include <cerrno>
38#include <cstring>
39
40#include "version.h"
41
43
45
47namespace csv
48{
51
53 static constexpr struct
54 {
55 int major {CSVPP_VERSION_MAJOR},
56 minor {CSVPP_VERSION_MINOR},
57 patch {CSVPP_VERSION_PATCH};
58 } version;
59
61
63 class Error: virtual public std::exception
64 {
65 public:
66 virtual ~Error() = default;
68 const char * what() const throw() override { return msg_.c_str(); }
69 protected:
70 Error() = default;
71 explicit Error(const std::string & msg): msg_{msg} {}
72 private:
73 std::string msg_;
74 };
75
77
79 struct Internal_error: virtual public Error
80 {
82 explicit Internal_error(const std::string & msg): Error{msg} {}
83 };
84
86
88 class Parse_error final: virtual public Error
89 {
90 public:
94 Parse_error(const std::string & type, int line_no, int col_no):
95 Error{"Error parsing CSV at line: " +
96 std::to_string(line_no) + ", col: " + std::to_string(col_no) + ": " + type},
97 type_{type},
98 line_no_{line_no},
99 col_no_{col_no}
100 {}
101
103 std::string type() const { return type_; }
105 int line_no() const { return line_no_; }
107 int col_no() const { return col_no_; }
108
109 private:
110 std::string type_;
111 int line_no_;
112 int col_no_;
113 };
114
116
118 struct Out_of_range_error final: virtual public Error
119 {
121 explicit Out_of_range_error(const std::string & msg): Error{msg} {}
122 };
123
125
127 struct Type_conversion_error final: virtual public Error
128 {
129 public:
131 explicit Type_conversion_error(const std::string & field):
132 Error{"Could not convert '" + field + "' to requested type"},
133 field_(field)
134 {}
135
137 std::string field() const { return field_; }
138 private:
139 std::string field_;
140 };
141
143
145 class IO_error final: virtual public Error
146 {
147 public:
150 explicit IO_error(const std::string & msg, int errno_code):
151 Error{msg + ": " + std::strerror(errno_code)},
152 errno_code_{errno_code}
153 {}
155 int errno_code() const { return errno_code_; }
157 std::string errno_str() const { return std::strerror(errno_code_); }
158 private:
159 int errno_code_;
160 };
161
162 namespace detail
163 {
164 // SFINAE types to determine the best way to convert a given type to a std::string
165
166 // Does the type support std::to_string?
167 template <typename T, typename = void>
168 struct has_std_to_string: std::false_type{};
169 template <typename T>
170 struct has_std_to_string<T, std::void_t<decltype(std::to_string(std::declval<T>()))>> : std::true_type{};
171 template <typename T>
172 inline constexpr bool has_std_to_string_v = has_std_to_string<T>::value;
173
174 // Does the type support a custom to_string?
175 template <typename T, typename = void>
176 struct has_to_string: std::false_type{};
177 template <typename T>
178 struct has_to_string<T, std::void_t<decltype(to_string(std::declval<T>()))>> : std::true_type{};
179 template <typename T>
180 inline constexpr bool has_to_string_v = has_to_string<T>::value;
181
182 // Does the type support conversion via std::ostream::operator>>
183 template <typename T, typename = void>
184 struct has_ostr: std::false_type{};
185 template <typename T>
186 struct has_ostr<T, std::void_t<decltype(std::declval<std::ostringstream&>() << std::declval<T>())>> : std::true_type{};
187 template <typename T>
188 inline constexpr bool has_ostr_v = has_ostr<T>::value;
189 };
190
192
196 template <typename T, typename std::enable_if_t<std::is_convertible_v<T, std::string>, int> = 0>
197 std::string str(const T & t)
198 {
199 return t;
200 }
201
202 template <typename T, typename std::enable_if_t<!std::is_convertible_v<T, std::string> && detail::has_std_to_string_v<T>, int> = 0>
203 std::string str(const T & t)
204 {
205 return std::to_string(t);
206 }
207
208 template <typename T, typename std::enable_if_t<!std::is_convertible_v<T, std::string> && !detail::has_std_to_string_v<T> && detail::has_to_string_v<T>, int> = 0>
209 std::string str(const T & t)
210 {
211 return to_string(t);
212 }
213
214 template <typename T, typename std::enable_if_t<!std::is_convertible_v<T, std::string> && !detail::has_std_to_string_v<T> && !detail::has_to_string_v<T> && detail::has_ostr_v<T>, int> = 0>
215 std::string str(const T & t)
216 {
217 std::ostringstream os;
218 os<<t;
219 return os.str();
220 }
221
222 // special conversion for char using std::string's initializer list ctor
223 std::string str(char c)
224 {
225 return {c};
226 }
227
229
242 class Reader
243 {
244 public:
246
249 class Row
250 {
251 public:
253
255 template <typename T = std::string> class Iterator
256 {
257 public:
258 using value_type = T;
259 using difference_type = std::ptrdiff_t;
260 using pointer = const T*;
261 using reference = const T&;
262 using iterator_category = std::input_iterator_tag;
263
265
267 Iterator(): row_{nullptr} {}
268
270
276 explicit Iterator(Reader::Row & row): row_{&row}
277 {
278 ++*this;
279 }
280
282 const T & operator*() const { return obj_; }
283
285 const T * operator->() const { return &obj_; }
286
288
292 Iterator & operator++()
293 {
294 assert(row_);
295
296 if(end_of_row_)
297 {
298 row_ = nullptr;
299 }
300 else
301 {
302 obj_ = row_->read_field<T>();
303
304 if(row_->end_of_row())
305 end_of_row_ = true;
306 }
307
308 return *this;
309 }
310
312 bool equals(const Iterator<T> & rhs) const
313 {
314 return row_ == rhs.row_;
315 }
316
317 private:
318 Reader::Row * row_;
319 T obj_{};
320 bool end_of_row_ = false;
321 };
322
324
326 template<typename T = std::string>
327 class Range
328 {
329 public:
332 {
333 return Row::Iterator<T>{row_};
334 }
335
338 {
339 return Row::Iterator<T>{};
340 }
341 private:
342 friend Row;
343 explicit Range(Row & row):row_{row} {}
344 Row & row_;
345 };
346
349 template<typename T = std::string>
351 {
352 return Row::Iterator<T>{*this};
353 }
354
357 template<typename T = std::string>
359 {
360 return Row::Iterator<T>{};
361 }
362
364
368 template<typename T = std::string>
370 {
371 return Range<T>{*this};
372 }
373
375
381 template<typename T = std::string>
383 {
384 assert(reader_);
385
386 if(end_of_row_)
387 {
388 past_end_of_row_ = true;
389 return T{};
390 }
391
392 auto field = reader_->read_field<T>();
393
394 if(reader_->end_of_row())
395 end_of_row_ = true;
396
397 return field;
398 }
399
401
408 template<typename T>
409 Row & operator>>(T & data)
410 {
411 data = read_field<T>();
412 return * this;
413 }
414
416
422 template<typename T = std::string, typename OutputIter>
423 void read(OutputIter it)
424 {
425 std::copy(begin<T>(), end<T>(), it);
426 }
427
429
435 template<typename T = std::string>
436 std::vector<T> read_vec()
437 {
438 std::vector<T> vec;
439 std::copy(begin<T>(), end<T>(), std::back_inserter(vec));
440 return vec;
441 }
442
444
452 template <typename ... Args>
453 std::tuple<Args...> read_tuple()
454 {
455 std::tuple<Args...> ret;
456 read_tuple_helper(ret, std::index_sequence_for<Args...>{});
457 return ret;
458 }
459
461
468 template <typename ... Data>
469 void read_v(Data & ... data)
470 {
471 (void)(*this >> ... >> data);
472 }
473
475 bool end_of_row() const { return end_of_row_; }
476
478 operator bool() { return reader_ && !past_end_of_row_; }
479
480 private:
481 friend Reader;
482
484
487 template <typename Tuple, std::size_t ... Is>
488 void read_tuple_helper(Tuple & t, std::index_sequence<Is...>)
489 {
490 ((*this >> std::get<Is>(t)), ...);
491 }
492
494
496 Row(): reader_{nullptr} {}
497
499
501 explicit Row(Reader & reader): reader_{&reader} {}
502
503 Reader * reader_ { nullptr };
504 bool end_of_row_ { false };
505 bool past_end_of_row_ { false };
506 };
507
510 {
511 public:
512 using value_type = Row;
513 using difference_type = std::ptrdiff_t;
514 using pointer = const value_type*;
515 using reference = const value_type&;
516 using iterator_category = std::input_iterator_tag;
517
519
521 Iterator(): reader_{nullptr} {}
522
524
527 explicit Iterator(Reader & r): reader_{&r}
528 {
529 obj_ = reader_->get_row();
530
531 if(!*reader_)
532 reader_ = nullptr;
533 }
534
536 const value_type & operator*() const { return obj_; }
537
539 value_type & operator*() { return obj_; }
540
542 const value_type * operator->() const { return &obj_; }
543
545 value_type * operator->() { return &obj_; }
546
549 {
550 // discard any remaining fields
551 while(!obj_.end_of_row())
552 obj_.read_field();
553
554 assert(reader_);
555 obj_ = reader_->get_row();
556
557 if(!*reader_)
558 reader_ = nullptr;
559
560 return *this;
561 }
562
564 bool equals(const Iterator & rhs) const
565 {
566 return reader_ == rhs.reader_;
567 }
568
569 private:
570 Reader * reader_ { nullptr };
571 value_type obj_;
572 };
573
575
581 explicit Reader(std::istream & input_stream,
582 const char delimiter = ',', const char quote = '"',
583 const bool lenient = false):
584 input_stream_{&input_stream},
585 delimiter_{delimiter},
586 quote_{quote},
587 lenient_{lenient}
588 {}
589
591
597 explicit Reader(const std::string & filename,
598 const char delimiter = ',', const char quote = '"',
599 const bool lenient = false):
600 internal_input_stream_{std::make_unique<std::ifstream>(filename)},
601 input_stream_{internal_input_stream_.get()},
602 delimiter_{delimiter},
603 quote_{quote},
604 lenient_{lenient}
605 {
606 if(!(*internal_input_stream_))
607 throw IO_error("Could not open file '" + filename + "'", errno);
608 }
609
611
615
617
620 static inline constexpr input_string_t input_string{};
621
623
630 Reader(input_string_t, const std::string & input_data,
631 const char delimiter = ',', const char quote = '"',
632 const bool lenient = false):
633 internal_input_stream_{std::make_unique<std::istringstream>(input_data)},
634 input_stream_{internal_input_stream_.get()},
635 delimiter_{delimiter},
636 quote_{quote},
637 lenient_{lenient}
638 {}
639
640 ~Reader() = default;
641
642 Reader(const Reader &) = delete;
643 Reader & operator=(const Reader &) = delete;
644 Reader(Reader &&) = default;
645 Reader & operator=(Reader &&) = default;
646
648
650 bool end_of_row() const { return end_of_row_ || eof(); }
651
653
655 bool eof() const { return state_ == State::eof; }
656
658 operator bool() { return !eof(); }
659
661
663 void set_delimiter(const char delimiter) { delimiter_ = delimiter; }
665
667 void set_quote(const char quote) { quote_ = quote; }
668
670
673 void set_lenient(const bool lenient) { lenient_ = lenient; }
674
677 {
678 return Iterator(*this);
679 }
680
682 auto end()
683 {
684 return Iterator();
685 }
686
688
695 template<typename T = std::string>
697 {
698 if(eof())
699 return {};
700
701 end_of_row_ = false;
702
703 std::string field;
704 if(conversion_retry_)
705 {
706 field = *conversion_retry_;
707 conversion_retry_.reset();
708 }
709 else
710 {
711 field = parse();
712 }
713
714 // no conversion needed for strings
715 if constexpr(std::is_convertible_v<std::string, T>)
716 {
717 return field;
718 }
719 else
720 {
721 T field_val{};
722 std::istringstream convert(field);
723 convert>>field_val;
724 if(!convert || convert.peek() != std::istream::traits_type::eof())
725 {
726 conversion_retry_ = field;
727 throw Type_conversion_error(field);
728 }
729
730 return field_val;
731 }
732 }
733
735
742 template<typename T>
743 Reader & operator>>(T & data)
744 {
745 data = read_field<T>();
746 return * this;
747 }
748
750
760 template <typename ... Data>
761 void read_v(Data & ... data)
762 {
763 (void)(*this >> ... >> data);
764 }
765
767
770 {
771 consume_newlines();
772 if(eof())
773 return Row{};
774 else
775 return Row{*this};
776 }
777
779
786 template <typename T = std::string, typename OutputIter>
787 bool read_row(OutputIter it)
788 {
789 auto row = get_row();
790 if(!row)
791 return false;
792
793 row.read<T>(it);
794 return true;
795 }
796
798
804 template <typename T = std::string>
805 std::optional<std::vector<T>> read_row_vec()
806 {
807 auto row = get_row();
808 if(!row)
809 return {};
810
811 return row.read_vec<T>();
812 }
813
815
824 template <typename ... Args>
825 std::optional<std::tuple<Args...>> read_row_tuple()
826 {
827 auto row = get_row();
828 if(!row)
829 return {};
830
831 return row.read_tuple<Args...>();
832 }
833
835
841 template <typename T = std::string>
842 std::vector<std::vector<T>> read_all()
843 {
844 std::vector<std::vector<T>> data;
845 while(true)
846 {
847 auto row = read_row_vec<T>();
848 if(row)
849 data.push_back(*row);
850 else
851 break;
852 }
853 return data;
854 }
855
856 private:
857
859
863 int getc()
864 {
865 int c = input_stream_->get();
866 if(input_stream_->bad() && !input_stream_->eof())
867 throw IO_error{"Error reading from input", errno};
868
869 if(c == '\n')
870 {
871 ++line_no_;
872 col_no_ = 0;
873 }
874 else if(c != std::istream::traits_type::eof())
875 ++col_no_;
876
877 return c;
878 }
879
881
884 void consume_newlines()
885 {
886 if(state_ != State::consume_newlines)
887 return;
888
889 while(true)
890 {
891 if(int c = getc(); c == std::istream::traits_type::eof())
892 {
893 end_of_row_ = true;
894 state_ = State::eof;
895 break;
896 }
897 else if(c != '\r' && c != '\n')
898 {
899 state_ = State::read;
900 input_stream_->unget();
901 --col_no_;
902 break;
903 }
904 }
905 }
906
908
913 std::string parse()
914 {
915 consume_newlines();
916
917 if(eof())
918 return {};
919
920 bool quoted = false;
921 std::string field;
922
923 bool field_done = false;
924 while(!field_done)
925 {
926 int c = getc();
927 bool c_done = false;
928 while(!c_done)
929 {
930 switch(state_)
931 {
932 case State::quote:
933 // end of the field?
934 if(c == delimiter_ || c == '\n' || c == '\r' || c == std::istream::traits_type::eof())
935 {
936 quoted = false;
937 state_ = State::read;
938 break;
939 }
940 // if it's not an escaped quote, then it's an error
941 else if(c == quote_)
942 {
943 field += c;
944 state_ = State::read;
945 c_done = true;
946 break;
947 }
948 else if(lenient_)
949 {
950 field += quote_;
951 field += c;
952 state_ = State::read;
953 c_done = true;
954 break;
955 }
956 else
957 throw Parse_error("Unescaped quote", line_no_, col_no_ - 1);
958
959 case State::read:
960 // we need special handling for quotes
961 if(c == quote_)
962 {
963 if(quoted)
964 {
965 state_ = State::quote;
966 c_done = true;
967 break;
968 }
969 else
970 {
971 if(field.empty())
972 {
973 quoted = true;
974 c_done = true;
975 break;
976 }
977 else if(!lenient_)
978 {
979 // quotes are not allowed inside of an unquoted field
980 throw Parse_error("quote found in unquoted field", line_no_, col_no_);
981 }
982 }
983 }
984
985 if(quoted && c == std::istream::traits_type::eof())
986 {
987 if(lenient_)
988 {
989 end_of_row_ = field_done = c_done = true;
990 state_ = State::consume_newlines;
991 break;
992 }
993 else
994 throw Parse_error("Unterminated quoted field - reached end-of-file", line_no_, col_no_);
995 }
996 else if(!quoted && c == delimiter_)
997 {
998 field_done = c_done = true;
999 break;
1000 }
1001 else if(!quoted && (c == '\n' || c == '\r' || c == std::istream::traits_type::eof()))
1002 {
1003 end_of_row_ = field_done = c_done = true;
1004 state_ = State::consume_newlines;
1005 break;
1006 }
1007
1008 field += c;
1009 c_done = true;
1010 break;
1011
1012 default:
1013 // It should not be possible to reach this state
1014 throw Internal_error{"Illegal state"};
1015 }
1016 }
1017 }
1018 return field;
1019 }
1020
1024 std::unique_ptr<std::istream> internal_input_stream_;
1025
1029 std::istream * input_stream_;
1030
1031 char delimiter_ {','};
1032 char quote_ {'"'};
1033 bool lenient_ { false };
1034
1035 std::optional<std::string> conversion_retry_;
1036 bool end_of_row_ { false };
1037
1039 enum class State
1040 {
1041 read,
1042 quote,
1043 consume_newlines,
1044 eof
1045 };
1046
1048 State state_ { State::consume_newlines };
1049
1050 unsigned int line_no_ { 1 };
1051 unsigned int col_no_ { 0 };
1052 };
1053
1055 inline bool operator==(const Reader::Iterator & lhs, const Reader::Iterator & rhs)
1056 {
1057 return lhs.equals(rhs);
1058 }
1059
1061 inline bool operator!=(const Reader::Iterator & lhs, const Reader::Iterator & rhs)
1062 {
1063 return !lhs.equals(rhs);
1064 }
1065
1067 template <typename T>
1069 {
1070 return lhs.equals(rhs);
1071 }
1072
1074 template <typename T>
1076 {
1077 return !lhs.equals(rhs);
1078 }
1079
1081
1086 template <typename Header = std::string, typename Value = std::string>
1088 {
1089 private:
1090 std::unique_ptr<Reader> reader_;
1091 Value default_val_;
1092 std::vector<Header> headers_;
1093 std::map<Header, Value> obj_;
1094
1096
1103 std::vector<Header> get_header_row(const std::vector<Header> & headers)
1104 {
1105 if(!std::empty(headers))
1106 return headers;
1107
1108 auto header_row = reader_->read_row_vec<Header>();
1109 if(header_row)
1110 return *header_row;
1111 else
1112 throw Parse_error("Can't get header row", 0, 0);
1113 }
1114
1115 template <typename Header2, typename Value2>
1116 friend class Map_reader_iter;
1117
1118 public:
1120
1123
1125
1140 explicit Map_reader_iter(std::istream & input_stream, const Value & default_val = {}, const std::vector<Header> & headers = {},
1141 const char delimiter = ',', const char quote = '"',
1142 const bool lenient = false):
1143 reader_{std::make_unique<Reader>(input_stream, delimiter, quote, lenient)},
1144 default_val_{default_val},
1145 headers_{get_header_row(headers)}
1146 {
1147 ++(*this);
1148 }
1149
1151
1164 explicit Map_reader_iter(const std::string & filename, const Value & default_val = {}, const std::vector<Header> & headers = {},
1165 const char delimiter = ',', const char quote = '"',
1166 const bool lenient = false):
1167 reader_{std::make_unique<Reader>(filename, delimiter, quote, lenient)},
1168 default_val_{default_val},
1169 headers_{get_header_row(headers)}
1170 {
1171 ++(*this);
1172 }
1173
1175
1190 Map_reader_iter(Reader::input_string_t, const std::string & input_data, const Value & default_val = {}, const std::vector<Header> & headers = {},
1191 const char delimiter = ',', const char quote = '"',
1192 const bool lenient = false):
1193 reader_{std::make_unique<Reader>(Reader::input_string, input_data, delimiter, quote, lenient)},
1194 default_val_{default_val},
1195 headers_{get_header_row(headers)}
1196 {
1197 ++(*this);
1198 }
1199
1200 using value_type = std::map<Header, Value>;
1201 using difference_type = std::ptrdiff_t;
1202 using pointer = const value_type*;
1203 using reference = const value_type&;
1204 using iterator_category = std::input_iterator_tag;
1205
1207 const value_type & operator*() const { return obj_; }
1208 value_type & operator*() { return obj_; }
1209
1211 const value_type * operator->() const { return &obj_; }
1212 value_type * operator->() { return &obj_; }
1213
1215
1220 const typename value_type::mapped_type & operator[](const typename value_type::key_type & key) const { return obj_.at(key); }
1221 typename value_type::mapped_type & operator[](const typename value_type::key_type & key) { return obj_.at(key); }
1222
1224
1229 {
1230 auto row = reader_->read_row_vec<Value>();
1231 if(!row)
1232 reader_.reset();
1233 else
1234 {
1235 if(std::size(*row) > std::size(headers_))
1236 throw Out_of_range_error("Too many fields");
1237
1238 for(std::size_t i = 0; i < std::size(*row); ++i)
1239 obj_[headers_[i]] = (*row)[i];
1240
1241 for(std::size_t i = std::size(*row); i < std::size(headers_); ++i)
1242 obj_[headers_[i]] = default_val_;
1243 }
1244
1245 return *this;
1246 }
1247
1249 template <typename Header2, typename Value2>
1251 {
1252 return reader_ == rhs.reader_;
1253 }
1254
1256
1258 std::vector<Header> get_headers() const
1259 {
1260 return headers_;
1261 }
1262 };
1263
1265 template <typename Header1, typename Value1, typename Header2, typename Value2>
1267 {
1268 return lhs.equals(rhs);
1269 }
1270
1272 template <typename Header1, typename Value1, typename Header2, typename Value2>
1274 {
1275 return !lhs.equals(rhs);
1276 }
1277
1279
1285 {
1286 public:
1288
1291 {
1292 public:
1293 using value_type = void;
1294 using difference_type = void;
1295 using pointer = void;
1296 using reference = void;
1297 using iterator_category = std::output_iterator_tag;
1298
1300
1302 explicit Iterator(Writer & w): writer_{w} {}
1303
1305 Iterator & operator*() { return *this; }
1307 Iterator & operator++() { return *this; }
1309 Iterator & operator++(int) { return *this; }
1310
1312
1316 template <typename T>
1317 Iterator & operator=(const T & field)
1318 {
1319 writer_.write_field(field);
1320 return *this;
1321 }
1322
1323 private:
1324 Writer & writer_;
1325 };
1326
1328
1333 explicit Writer(std::ostream & output_stream,
1334 const char delimiter = ',', const char quote = '"'):
1335 output_stream_{&output_stream},
1336 delimiter_{delimiter},
1337 quote_{quote}
1338 {}
1339
1341
1347 explicit Writer(const std::string& filename,
1348 const char delimiter = ',', const char quote = '"'):
1349 internal_output_stream_{std::make_unique<std::ofstream>(filename, std::ios::binary)},
1350 output_stream_{internal_output_stream_.get()},
1351 delimiter_{delimiter},
1352 quote_{quote}
1353 {
1354 if(!(*internal_output_stream_))
1355 throw IO_error("Could not open file '" + filename + "'", errno);
1356 }
1357
1359
1362 {
1363 if(!start_of_row_)
1364 {
1365 // try to end the row, but ignore any IO errors
1366 try { end_row(); }
1367 catch(const IO_error & e) {}
1368 }
1369 }
1370
1371 Writer(const Writer &) = delete;
1372 Writer(Writer &&) = default;
1373 Writer & operator=(const Writer &) = delete;
1374 Writer & operator=(Writer &&) = default;
1375
1377
1381 {
1382 return Iterator(*this);
1383 }
1384
1386
1388 void set_delimiter(const char delimiter) { delimiter_ = delimiter; }
1389
1391
1393 void set_quote(const char quote) { quote_ = quote; }
1394
1396
1400 template<typename T>
1401 void write_field(const T & field)
1402 {
1403 if(!start_of_row_)
1404 {
1405 (*output_stream_)<<delimiter_;
1406 if(output_stream_->bad())
1407 throw IO_error{"Error writing to output", errno};
1408 }
1409
1410 (*output_stream_)<<quote(field);
1411 if(output_stream_->bad())
1412 throw IO_error{"Error writing to output", errno};
1413
1414 start_of_row_ = false;
1415 }
1416
1418
1422 template<typename T>
1423 Writer & operator<<(const T & field)
1424 {
1425 write_field(field);
1426 return *this;
1427 }
1428
1430
1434 {
1435 manip(*this);
1436 return *this;
1437 }
1438
1440
1442 void end_row()
1443 {
1444 (*output_stream_)<<"\r\n";
1445 if(output_stream_->bad())
1446 throw IO_error{"Error writing to output", errno};
1447 start_of_row_ = true;
1448 }
1449
1456 template<typename Iter>
1457 void write_fields(Iter first, Iter last)
1458 {
1459 for(; first != last; ++first)
1460 write_field(*first);
1461 }
1462
1468 template<typename T>
1469 void write_fields(const std::initializer_list<T> & data)
1470 {
1471 write_fields(std::begin(data), std::end(data));
1472 }
1473
1475
1482 template<typename Range>
1483 void write_fields(const Range & data)
1484 {
1485 write_fields(std::begin(data), std::end(data));
1486 }
1487
1489
1493 template<typename ...Data>
1494 void write_fields_v(const Data & ...data)
1495 {
1496 (void)(*this << ... << data);
1497 }
1498
1500
1505 template<typename ...Args>
1506 void write_fields(const std::tuple<Args...> & data)
1507 {
1508 std::apply(&Writer::write_fields_v<Args...>, std::tuple_cat(std::tuple(std::ref(*this)), data));
1509 }
1510
1517 template<typename Iter>
1518 void write_row(Iter first, Iter last)
1519 {
1520 write_fields(first, last);
1521
1522 end_row();
1523 }
1524
1530 template<typename T>
1531 void write_row(const std::initializer_list<T> & data)
1532 {
1533 write_row(std::begin(data), std::end(data));
1534 }
1535
1537
1544 template<typename Range>
1545 void write_row(const Range & data)
1546 {
1547 write_row(std::begin(data), std::end(data));
1548 }
1549
1551
1555 template<typename ...Data>
1556 void write_row_v(const Data & ...data)
1557 {
1558 (void)(*this << ... << data);
1559
1560 end_row();
1561 }
1562
1564
1569 template<typename ...Args>
1570 void write_row(const std::tuple<Args...> & data)
1571 {
1572 std::apply(&Writer::write_row_v<Args...>, std::tuple_cat(std::tuple(std::ref(*this)), data));
1573 }
1574
1575 private:
1577
1581 template<typename T>
1582 std::string quote(const T & field)
1583 {
1584 std::string field_str = str(field);
1585
1586 std::string ret{quote_};
1587 ret.reserve(std::size(field_str) + 2);
1588 bool quoted = false;
1589 for(auto c: field_str)
1590 {
1591 if(c == quote_)
1592 {
1593 quoted = true;
1594 ret += quote_;
1595 }
1596 else if(c == delimiter_ || c == '\r' || c == '\n')
1597 quoted = true;
1598
1599 ret += c;
1600 }
1601
1602 if(!quoted)
1603 return field_str;
1604
1605 ret += quote_;
1606 return ret;
1607 }
1608
1609 friend Writer &end_row(Writer & w);
1610
1612 std::unique_ptr<std::ostream> internal_output_stream_;
1613
1617 std::ostream * output_stream_;
1618 bool start_of_row_ {true};
1619
1620 char delimiter_ {','};
1621 char quote_ {'"'};
1622 };
1623
1625
1628 inline Writer & end_row(Writer & w)
1629 {
1630 w.end_row();
1631 return w;
1632 }
1633
1635
1637 template <typename Header, typename Default_value = std::string>
1639 {
1640 private:
1641 std::unique_ptr<Writer> writer_;
1642 std::vector<Header> headers_;
1643 Default_value default_val_;
1644
1645 public:
1647
1654 Map_writer_iter(std::ostream & output_stream, const std::vector<Header> & headers, const Default_value & default_val = {},
1655 const char delimiter = ',', const char quote = '"'):
1656 writer_{std::make_unique<Writer>(output_stream, delimiter, quote)}, headers_{headers}, default_val_{default_val}
1657 {
1658 writer_->write_row(headers);
1659 }
1660
1662
1670 Map_writer_iter(const std::string& filename, const std::vector<Header> & headers, const Default_value & default_val = {},
1671 const char delimiter = ',', const char quote = '"'):
1672 writer_{std::make_unique<Writer>(filename, delimiter, quote)}, headers_{headers}, default_val_{default_val}
1673 {
1674 writer_->write_row(headers);
1675 }
1676
1677 using value_type = void;
1678 using difference_type = void;
1679 using pointer = void;
1680 using reference = void;
1681 using iterator_category = std::output_iterator_tag;
1682
1684 Map_writer_iter & operator*() { return *this; }
1686 Map_writer_iter & operator++() { return *this; }
1688 Map_writer_iter & operator++(int) { return *this; }
1689
1691
1697 template <typename K, typename T, typename std::enable_if_t<std::is_convertible_v<Header, K>, int> = 0>
1698 Map_writer_iter & operator=(const std::map<K, T> & row)
1699 {
1700 for(auto & h: headers_)
1701 {
1702 try
1703 {
1704 (*writer_)<<row.at(h);
1705 }
1706 catch(std::out_of_range&)
1707 {
1708 (*writer_)<<default_val_;
1709 }
1710 }
1711
1712 writer_->end_row();
1713 return *this;
1714 }
1715 };
1717};
1718
1719#endif // CSV_HPP
Error base class.
Definition csv.hpp:64
const char * what() const override
Definition csv.hpp:68
IO error.
Definition csv.hpp:146
IO_error(const std::string &msg, int errno_code)
Definition csv.hpp:150
std::string errno_str() const
Definition csv.hpp:157
int errno_code() const
Definition csv.hpp:155
Map-based Reader iterator.
Definition csv.hpp:1088
const value_type & operator*() const
Definition csv.hpp:1207
Map_reader_iter(std::istream &input_stream, const Value &default_val={}, const std::vector< Header > &headers={}, const char delimiter=',', const char quote='"', const bool lenient = false)
Open a std::istream for CSV parsing.
Definition csv.hpp:1140
Map_reader_iter & operator++()
Iterate to next field.
Definition csv.hpp:1228
Map_reader_iter()
Empty constructor.
Definition csv.hpp:1122
bool equals(const Map_reader_iter< Header2, Value2 > &rhs) const
Compare to another Map_reader_iter.
Definition csv.hpp:1250
std::vector< Header > get_headers() const
Get the headers.
Definition csv.hpp:1258
const value_type * operator->() const
Definition csv.hpp:1211
Map_reader_iter(const std::string &filename, const Value &default_val={}, const std::vector< Header > &headers={}, const char delimiter=',', const char quote='"', const bool lenient = false)
Open a file for CSV parsing.
Definition csv.hpp:1164
const value_type::mapped_type & operator[](const typename value_type::key_type &key) const
Get a field from the current row.
Definition csv.hpp:1220
Map_reader_iter(Reader::input_string_t, const std::string &input_data, const Value &default_val={}, const std::vector< Header > &headers={}, const char delimiter=',', const char quote='"', const bool lenient = false)
Parse CSV from memory.
Definition csv.hpp:1190
Map-based Writer iterator.
Definition csv.hpp:1639
Map_writer_iter & operator++(int)
No-op.
Definition csv.hpp:1688
Map_writer_iter(std::ostream &output_stream, const std::vector< Header > &headers, const Default_value &default_val={}, const char delimiter=',', const char quote='"')
Use a std::ostream for CSV output.
Definition csv.hpp:1654
Map_writer_iter & operator*()
No-op.
Definition csv.hpp:1684
Map_writer_iter & operator++()
No-op.
Definition csv.hpp:1686
Map_writer_iter(const std::string &filename, const std::vector< Header > &headers, const Default_value &default_val={}, const char delimiter=',', const char quote='"')
Open a file for CSV output.
Definition csv.hpp:1670
Map_writer_iter & operator=(const std::map< K, T > &row)
Write a row.
Definition csv.hpp:1698
Parsing error.
Definition csv.hpp:89
std::string type() const
Definition csv.hpp:103
int col_no() const
Definition csv.hpp:107
int line_no() const
Definition csv.hpp:105
Parse_error(const std::string &type, int line_no, int col_no)
Definition csv.hpp:94
Iterates over Rows in CSV data.
Definition csv.hpp:510
const value_type * operator->() const
Definition csv.hpp:542
const value_type & operator*() const
Definition csv.hpp:536
bool equals(const Iterator &rhs) const
Compare to another Reader::Iterator.
Definition csv.hpp:564
Iterator & operator++()
Iterate to next Row.
Definition csv.hpp:548
value_type & operator*()
Definition csv.hpp:539
value_type * operator->()
Definition csv.hpp:545
Iterator(Reader &r)
Creates an iterator from a Reader object.
Definition csv.hpp:527
Iterator()
Empty constructor.
Definition csv.hpp:521
Iterates over the fields within a Row.
Definition csv.hpp:256
bool equals(const Iterator< T > &rhs) const
Compare to another Reader::Row::Iterator.
Definition csv.hpp:312
Helper class for iterating over a Row. Use Row::range to obtain.
Definition csv.hpp:328
Row::Iterator< T > begin()
Definition csv.hpp:331
Row::Iterator< T > end()
Definition csv.hpp:337
Represents a single row of CSV data.
Definition csv.hpp:250
std::vector< T > read_vec()
Reads row into a std::vector.
Definition csv.hpp:436
std::tuple< Args... > read_tuple()
Reads row into a tuple.
Definition csv.hpp:453
void read(OutputIter it)
Reads row into an output iterator.
Definition csv.hpp:423
Row & operator>>(T &data)
Read a single field from the row.
Definition csv.hpp:409
Row::Iterator< T > end()
Definition csv.hpp:358
Range< T > range()
Range helper.
Definition csv.hpp:369
T read_field()
Read a single field from the row.
Definition csv.hpp:382
bool end_of_row() const
Definition csv.hpp:475
Row::Iterator< T > begin()
Definition csv.hpp:350
void read_v(Data &... data)
Reads row into variadic arguments.
Definition csv.hpp:469
Parses CSV data.
Definition csv.hpp:243
Iterator begin()
Definition csv.hpp:676
Row get_row()
Get the current Row.
Definition csv.hpp:769
bool eof() const
Check for end of row.
Definition csv.hpp:655
void read_v(Data &... data)
Reads fields into variadic arguments.
Definition csv.hpp:761
std::optional< std::vector< T > > read_row_vec()
Reads current row into a std::vector.
Definition csv.hpp:805
bool end_of_row() const
Check for end of input.
Definition csv.hpp:650
T read_field()
Read a single field.
Definition csv.hpp:696
bool read_row(OutputIter it)
Reads current row into an output iterator.
Definition csv.hpp:787
auto end()
Definition csv.hpp:682
Reader(const std::string &filename, const char delimiter=',', const char quote='"', const bool lenient = false)
Open a file for CSV parsing.
Definition csv.hpp:597
Reader(input_string_t, const std::string &input_data, const char delimiter=',', const char quote='"', const bool lenient = false)
Parse CSV from memory.
Definition csv.hpp:630
void set_lenient(const bool lenient)
Enable / disable lenient parsing.
Definition csv.hpp:673
std::optional< std::tuple< Args... > > read_row_tuple()
Reads current row into a tuple.
Definition csv.hpp:825
Reader & operator>>(T &data)
Read a single field.
Definition csv.hpp:743
void set_quote(const char quote)
Change the quote character.
Definition csv.hpp:667
std::vector< std::vector< T > > read_all()
Read entire CSV data into a vector of vectors.
Definition csv.hpp:842
Reader(std::istream &input_stream, const char delimiter=',', const char quote='"', const bool lenient = false)
Use a std::istream for CSV parsing.
Definition csv.hpp:581
void set_delimiter(const char delimiter)
Change the delimiter character.
Definition csv.hpp:663
Output iterator for writing CSV data field-by-field.
Definition csv.hpp:1291
Iterator & operator*()
No-op.
Definition csv.hpp:1305
Iterator & operator=(const T &field)
Writes a field to the CSV output.
Definition csv.hpp:1317
Iterator(Writer &w)
Creates an iterator from a Writer object.
Definition csv.hpp:1302
Iterator & operator++()
No-op.
Definition csv.hpp:1307
Iterator & operator++(int)
No-op.
Definition csv.hpp:1309
CSV writer.
Definition csv.hpp:1285
void write_fields(const std::tuple< Args... > &data)
Write fields from a tuple, without ending the row.
Definition csv.hpp:1506
Writer(const std::string &filename, const char delimiter=',', const char quote='"')
Open a file for CSV output.
Definition csv.hpp:1347
void write_row(Iter first, Iter last)
Definition csv.hpp:1518
~Writer()
Destructor.
Definition csv.hpp:1361
Iterator iterator()
Get iterator.
Definition csv.hpp:1380
void write_fields(Iter first, Iter last)
Definition csv.hpp:1457
Writer & operator<<(const T &field)
Writes a field to the CSV output.
Definition csv.hpp:1423
void write_row(const Range &data)
Write a row from a range.
Definition csv.hpp:1545
void end_row()
End the current row.
Definition csv.hpp:1442
void write_row(const std::tuple< Args... > &data)
Write a row from a tuple.
Definition csv.hpp:1570
void write_fields_v(const Data &...data)
Write fields from the given variadic parameters, without ending the row.
Definition csv.hpp:1494
void set_delimiter(const char delimiter)
Change the delimiter character.
Definition csv.hpp:1388
void set_quote(const char quote)
Change the quote character.
Definition csv.hpp:1393
void write_row_v(const Data &...data)
Write a row from the given variadic parameters.
Definition csv.hpp:1556
void write_fields(const std::initializer_list< T > &data)
Definition csv.hpp:1469
Writer(std::ostream &output_stream, const char delimiter=',', const char quote='"')
Use a std::ostream for CSV output.
Definition csv.hpp:1333
void write_field(const T &field)
Writes a field to the CSV output.
Definition csv.hpp:1401
Writer & operator<<(Writer &(*manip)(Writer &))
Apply a stream manipulator to the CSV output.
Definition csv.hpp:1433
void write_row(const std::initializer_list< T > &data)
Definition csv.hpp:1531
void write_fields(const Range &data)
Write fields from a range, without ending the row.
Definition csv.hpp:1483
bool operator!=(const Reader::Iterator &lhs, const Reader::Iterator &rhs)
Compare two Reader::Iterator objects.
Definition csv.hpp:1061
bool operator==(const Reader::Iterator &lhs, const Reader::Iterator &rhs)
Compare two Reader::Iterator objects.
Definition csv.hpp:1055
std::string str(const T &t)
String conversion.
Definition csv.hpp:197
Writer & end_row(Writer &w)
End row stream manipulator for Writer.
Definition csv.hpp:1628
CSV library namespace.
Definition csv.hpp:48
Internal error.
Definition csv.hpp:80
Internal_error(const std::string &msg)
Definition csv.hpp:82
Out of range error.
Definition csv.hpp:119
Out_of_range_error(const std::string &msg)
Definition csv.hpp:121
Disambiguation tag type.
Definition csv.hpp:614
Type conversion error.
Definition csv.hpp:128
std::string field() const
Definition csv.hpp:137
Type_conversion_error(const std::string &field)
Definition csv.hpp:131