Path Tracer
rapidcsv.hpp
1 /*
2  * rapidcsv.h
3  *
4  * URL: https://github.com/d99kris/rapidcsv
5  * Version: 8.40
6  *
7  * Copyright (C) 2017-2020 Kristofer Berggren
8  * All rights reserved.
9  *
10  * rapidcsv is distributed under the BSD 3-Clause license, see LICENSE for details.
11  *
12  */
13 
14 #pragma once
15 
16 #include <algorithm>
17 #include <cassert>
18 #include <cmath>
19 #ifdef HAS_CODECVT
20 #include <codecvt>
21 #endif
22 #include <fstream>
23 #include <functional>
24 #include <iostream>
25 #include <map>
26 #include <sstream>
27 #include <string>
28 #include <typeinfo>
29 #include <vector>
30 
31 #if defined(_MSC_VER)
32 #include <BaseTsd.h>
33 typedef SSIZE_T ssize_t;
34 #endif
35 
36 namespace rapidcsv
37 {
38 #if defined(_MSC_VER)
39  static const bool sPlatformHasCR = true;
40 #else
41  static const bool sPlatformHasCR = false;
42 #endif
43 
49  {
58  explicit ConverterParams(const bool pHasDefaultConverter = false,
59  const long double pDefaultFloat = std::numeric_limits<long double>::signaling_NaN(),
60  const long long pDefaultInteger = 0)
61  : mHasDefaultConverter(pHasDefaultConverter)
62  , mDefaultFloat(pDefaultFloat)
63  , mDefaultInteger(pDefaultInteger)
64  {
65  }
66 
72 
76  long double mDefaultFloat;
77 
81  long long mDefaultInteger;
82  };
83 
88  class no_converter : public std::exception
89  {
94  virtual const char* what() const throw()
95  {
96  return "unsupported conversion datatype";
97  }
98  };
99 
105  template<typename T>
106  class Converter
107  {
108  public:
114  Converter(const ConverterParams& pConverterParams)
115  : mConverterParams(pConverterParams)
116  {
117  }
118 
124  void ToStr(const T& pVal, std::string& pStr) const
125  {
126  if (typeid(T) == typeid(int) ||
127  typeid(T) == typeid(long) ||
128  typeid(T) == typeid(long long) ||
129  typeid(T) == typeid(unsigned) ||
130  typeid(T) == typeid(unsigned long) ||
131  typeid(T) == typeid(unsigned long long) ||
132  typeid(T) == typeid(float) ||
133  typeid(T) == typeid(double) ||
134  typeid(T) == typeid(long double) ||
135  typeid(T) == typeid(char))
136  {
137  std::ostringstream out;
138  out << pVal;
139  pStr = out.str();
140  }
141  else
142  {
143  throw no_converter();
144  }
145  }
146 
152  void ToVal(const std::string& pStr, T& pVal) const
153  {
154  try
155  {
156  if (typeid(T) == typeid(int))
157  {
158  pVal = static_cast<T>(std::stoi(pStr));
159  return;
160  }
161  else if (typeid(T) == typeid(long))
162  {
163  pVal = static_cast<T>(std::stol(pStr));
164  return;
165  }
166  else if (typeid(T) == typeid(long long))
167  {
168  pVal = static_cast<T>(std::stoll(pStr));
169  return;
170  }
171  else if (typeid(T) == typeid(unsigned))
172  {
173  pVal = static_cast<T>(std::stoul(pStr));
174  return;
175  }
176  else if (typeid(T) == typeid(unsigned long))
177  {
178  pVal = static_cast<T>(std::stoul(pStr));
179  return;
180  }
181  else if (typeid(T) == typeid(unsigned long long))
182  {
183  pVal = static_cast<T>(std::stoull(pStr));
184  return;
185  }
186  }
187  catch (...)
188  {
189  if (!mConverterParams.mHasDefaultConverter)
190  {
191  throw;
192  }
193  else
194  {
195  pVal = static_cast<T>(mConverterParams.mDefaultInteger);
196  return;
197  }
198  }
199 
200  try
201  {
202  if (typeid(T) == typeid(float))
203  {
204  pVal = static_cast<T>(std::stof(pStr));
205  return;
206  }
207  else if (typeid(T) == typeid(double))
208  {
209  pVal = static_cast<T>(std::stod(pStr));
210  return;
211  }
212  else if (typeid(T) == typeid(long double))
213  {
214  pVal = static_cast<T>(std::stold(pStr));
215  return;
216  }
217  }
218  catch (...)
219  {
220  if (!mConverterParams.mHasDefaultConverter)
221  {
222  throw;
223  }
224  else
225  {
226  pVal = static_cast<T>(mConverterParams.mDefaultFloat);
227  return;
228  }
229  }
230 
231  if (typeid(T) == typeid(char))
232  {
233  pVal = static_cast<T>(pStr[0]);
234  return;
235  }
236  else
237  {
238  throw no_converter();
239  }
240  }
241 
242  private:
243  const ConverterParams& mConverterParams;
244  };
245 
251  template<>
252  inline void Converter<std::string>::ToStr(const std::string& pVal, std::string& pStr) const
253  {
254  pStr = pVal;
255  }
256 
262  template<>
263  inline void Converter<std::string>::ToVal(const std::string& pStr, std::string& pVal) const
264  {
265  pVal = pStr;
266  }
267 
268  template<typename T>
269  using ConvFunc = std::function<void (const std::string & pStr, T & pVal)>;
270 
275  struct LabelParams
276  {
286  explicit LabelParams(const int pColumnNameIdx = 0, const int pRowNameIdx = -1)
287  : mColumnNameIdx(pColumnNameIdx)
288  , mRowNameIdx(pRowNameIdx)
289  {
290  }
291 
296 
301  };
302 
307  {
320  explicit SeparatorParams(const char pSeparator = ',', const bool pTrim = false,
321  const bool pHasCR = sPlatformHasCR, const bool pQuotedLinebreaks = false,
322  const bool pAutoQuote = true)
323  : mSeparator(pSeparator)
324  , mTrim(pTrim)
325  , mHasCR(pHasCR)
326  , mQuotedLinebreaks(pQuotedLinebreaks)
327  , mAutoQuote(pAutoQuote)
328  {
329  }
330 
335 
339  bool mTrim;
340 
344  bool mHasCR;
345 
350 
355  };
356 
360  class Document
361  {
362  public:
372  explicit Document(const std::string& pPath = std::string(),
373  const LabelParams& pLabelParams = LabelParams(),
374  const SeparatorParams& pSeparatorParams = SeparatorParams(),
375  const ConverterParams& pConverterParams = ConverterParams())
376  : mPath(pPath)
377  , mLabelParams(pLabelParams)
378  , mSeparatorParams(pSeparatorParams)
379  , mConverterParams(pConverterParams)
380  {
381  if (!mPath.empty())
382  {
383  ReadCsv();
384  }
385  }
386 
395  explicit Document(std::istream& pStream,
396  const LabelParams& pLabelParams = LabelParams(),
397  const SeparatorParams& pSeparatorParams = SeparatorParams(),
398  const ConverterParams& pConverterParams = ConverterParams())
399  : mPath()
400  , mLabelParams(pLabelParams)
401  , mSeparatorParams(pSeparatorParams)
402  , mConverterParams(pConverterParams)
403  {
404  ReadCsv(pStream);
405  }
406 
412  void Load(const std::string& pPath)
413  {
414  mPath = pPath;
415  ReadCsv();
416  }
417 
422  void Load(std::istream& pStream)
423  {
424  mPath = "";
425  ReadCsv(pStream);
426  }
427 
434  void Save(const std::string& pPath = std::string())
435  {
436  if (!pPath.empty())
437  {
438  mPath = pPath;
439  }
440  WriteCsv();
441  }
442 
447  void Save(std::ostream& pStream)
448  {
449  WriteCsv(pStream);
450  }
451 
457  ssize_t GetColumnIdx(const std::string& pColumnName) const
458  {
459  if (mLabelParams.mColumnNameIdx >= 0)
460  {
461  if (mColumnNames.find(pColumnName) != mColumnNames.end())
462  {
463  return mColumnNames.at(pColumnName) - (mLabelParams.mRowNameIdx + 1);
464  }
465  }
466  return -1;
467  }
468 
474  template<typename T>
475  std::vector<T> GetColumn(const size_t pColumnIdx) const
476  {
477  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
478  std::vector<T> column;
479  Converter<T> converter(mConverterParams);
480  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
481  {
482  if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx)
483  {
484  T val;
485  converter.ToVal(itRow->at(columnIdx), val);
486  column.push_back(val);
487  }
488  }
489  return column;
490  }
491 
498  template<typename T>
499  std::vector<T> GetColumn(const size_t pColumnIdx, ConvFunc<T> pToVal) const
500  {
501  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
502  std::vector<T> column;
503  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
504  {
505  if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx)
506  {
507  T val;
508  pToVal(itRow->at(columnIdx), val);
509  column.push_back(val);
510  }
511  }
512  return column;
513  }
514 
520  template<typename T>
521  std::vector<T> GetColumn(const std::string& pColumnName) const
522  {
523  const ssize_t columnIdx = GetColumnIdx(pColumnName);
524  if (columnIdx < 0)
525  {
526  throw std::out_of_range("column not found: " + pColumnName);
527  }
528  return GetColumn<T>(columnIdx);
529  }
530 
537  template<typename T>
538  std::vector<T> GetColumn(const std::string& pColumnName, ConvFunc<T> pToVal) const
539  {
540  const ssize_t columnIdx = GetColumnIdx(pColumnName);
541  if (columnIdx < 0)
542  {
543  throw std::out_of_range("column not found: " + pColumnName);
544  }
545  return GetColumn<T>(columnIdx, pToVal);
546  }
547 
553  template<typename T>
554  void SetColumn(const size_t pColumnIdx, const std::vector<T>& pColumn)
555  {
556  const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
557 
558  while (pColumn.size() + (mLabelParams.mColumnNameIdx + 1) > GetDataRowCount())
559  {
560  std::vector<std::string> row;
561  row.resize(GetDataColumnCount());
562  mData.push_back(row);
563  }
564 
565  if ((columnIdx + 1) > GetDataColumnCount())
566  {
567  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
568  {
569  itRow->resize(columnIdx + 1 + (mLabelParams.mRowNameIdx + 1));
570  }
571  }
572 
573  Converter<T> converter(mConverterParams);
574  for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow)
575  {
576  std::string str;
577  converter.ToStr(*itRow, str);
578  mData.at(std::distance(pColumn.begin(), itRow) + (mLabelParams.mColumnNameIdx + 1)).at(columnIdx) = str;
579  }
580  }
581 
587  template<typename T>
588  void SetColumn(const std::string& pColumnName, const std::vector<T>& pColumn)
589  {
590  const ssize_t columnIdx = GetColumnIdx(pColumnName);
591  if (columnIdx < 0)
592  {
593  throw std::out_of_range("column not found: " + pColumnName);
594  }
595  SetColumn<T>(columnIdx, pColumn);
596  }
597 
602  void RemoveColumn(const size_t pColumnIdx)
603  {
604  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
605  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
606  {
607  itRow->erase(itRow->begin() + columnIdx);
608  }
609  }
610 
615  void RemoveColumn(const std::string& pColumnName)
616  {
617  ssize_t columnIdx = GetColumnIdx(pColumnName);
618  if (columnIdx < 0)
619  {
620  throw std::out_of_range("column not found: " + pColumnName);
621  }
622 
623  RemoveColumn(columnIdx);
624  }
625 
630  size_t GetColumnCount() const
631  {
632  const ssize_t count = static_cast<ssize_t>((mData.size() > 0) ? mData.at(0).size() : 0) -
633  (mLabelParams.mRowNameIdx + 1);
634  return (count >= 0) ? count : 0;
635  }
636 
642  ssize_t GetRowIdx(const std::string& pRowName) const
643  {
644  if (mLabelParams.mRowNameIdx >= 0)
645  {
646  if (mRowNames.find(pRowName) != mRowNames.end())
647  {
648  return mRowNames.at(pRowName) - (mLabelParams.mColumnNameIdx + 1);
649  }
650  }
651  return -1;
652  }
653 
659  template<typename T>
660  std::vector<T> GetRow(const size_t pRowIdx) const
661  {
662  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
663  std::vector<T> row;
664  Converter<T> converter(mConverterParams);
665  for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol)
666  {
667  if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx)
668  {
669  T val;
670  converter.ToVal(*itCol, val);
671  row.push_back(val);
672  }
673  }
674  return row;
675  }
676 
683  template<typename T>
684  std::vector<T> GetRow(const size_t pRowIdx, ConvFunc<T> pToVal) const
685  {
686  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
687  std::vector<T> row;
688  Converter<T> converter(mConverterParams);
689  for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol)
690  {
691  if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx)
692  {
693  T val;
694  pToVal(*itCol, val);
695  row.push_back(val);
696  }
697  }
698  return row;
699  }
700 
706  template<typename T>
707  std::vector<T> GetRow(const std::string& pRowName) const
708  {
709  ssize_t rowIdx = GetRowIdx(pRowName);
710  if (rowIdx < 0)
711  {
712  throw std::out_of_range("row not found: " + pRowName);
713  }
714  return GetRow<T>(rowIdx);
715  }
716 
723  template<typename T>
724  std::vector<T> GetRow(const std::string& pRowName, ConvFunc<T> pToVal) const
725  {
726  ssize_t rowIdx = GetRowIdx(pRowName);
727  if (rowIdx < 0)
728  {
729  throw std::out_of_range("row not found: " + pRowName);
730  }
731  return GetRow<T>(rowIdx, pToVal);
732  }
733 
739  template<typename T>
740  void SetRow(const size_t pRowIdx, const std::vector<T>& pRow)
741  {
742  const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
743 
744  while ((rowIdx + 1) > GetDataRowCount())
745  {
746  std::vector<std::string> row;
747  row.resize(GetDataColumnCount());
748  mData.push_back(row);
749  }
750 
751  if (pRow.size() > GetDataColumnCount())
752  {
753  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
754  {
755  itRow->resize(pRow.size() + (mLabelParams.mRowNameIdx + 1));
756  }
757  }
758 
759  Converter<T> converter(mConverterParams);
760  for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol)
761  {
762  std::string str;
763  converter.ToStr(*itCol, str);
764  mData.at(rowIdx).at(std::distance(pRow.begin(), itCol) + (mLabelParams.mRowNameIdx + 1)) = str;
765  }
766  }
767 
773  template<typename T>
774  void SetRow(const std::string& pRowName, const std::vector<T>& pRow)
775  {
776  ssize_t rowIdx = GetRowIdx(pRowName);
777  if (rowIdx < 0)
778  {
779  throw std::out_of_range("row not found: " + pRowName);
780  }
781  return SetRow<T>(rowIdx, pRow);
782  }
783 
788  void RemoveRow(const size_t pRowIdx)
789  {
790  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
791  mData.erase(mData.begin() + rowIdx);
792  }
793 
798  void RemoveRow(const std::string& pRowName)
799  {
800  ssize_t rowIdx = GetRowIdx(pRowName);
801  if (rowIdx < 0)
802  {
803  throw std::out_of_range("row not found: " + pRowName);
804  }
805 
806  RemoveRow(rowIdx);
807  }
808 
813  size_t GetRowCount() const
814  {
815  const ssize_t count = static_cast<ssize_t>(mData.size()) - (mLabelParams.mColumnNameIdx + 1);
816  return (count >= 0) ? count : 0;
817  }
818 
825  template<typename T>
826  T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const
827  {
828  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
829  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
830 
831  T val;
832  Converter<T> converter(mConverterParams);
833  converter.ToVal(mData.at(rowIdx).at(columnIdx), val);
834  return val;
835  }
836 
844  template<typename T>
845  T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc<T> pToVal) const
846  {
847  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
848  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
849 
850  T val;
851  pToVal(mData.at(rowIdx).at(columnIdx), val);
852  return val;
853  }
854 
861  template<typename T>
862  T GetCell(const std::string& pColumnName, const std::string& pRowName) const
863  {
864  const ssize_t columnIdx = GetColumnIdx(pColumnName);
865  if (columnIdx < 0)
866  {
867  throw std::out_of_range("column not found: " + pColumnName);
868  }
869 
870  const ssize_t rowIdx = GetRowIdx(pRowName);
871  if (rowIdx < 0)
872  {
873  throw std::out_of_range("row not found: " + pRowName);
874  }
875 
876  return GetCell<T>(columnIdx, rowIdx);
877  }
878 
886  template<typename T>
887  T GetCell(const std::string& pColumnName, const std::string& pRowName, ConvFunc<T> pToVal) const
888  {
889  const ssize_t columnIdx = GetColumnIdx(pColumnName);
890  if (columnIdx < 0)
891  {
892  throw std::out_of_range("column not found: " + pColumnName);
893  }
894 
895  const ssize_t rowIdx = GetRowIdx(pRowName);
896  if (rowIdx < 0)
897  {
898  throw std::out_of_range("row not found: " + pRowName);
899  }
900 
901  return GetCell<T>(columnIdx, rowIdx, pToVal);
902  }
903 
910  template<typename T>
911  T GetCell(const std::string& pColumnName, const size_t pRowIdx) const
912  {
913  const ssize_t columnIdx = GetColumnIdx(pColumnName);
914  if (columnIdx < 0)
915  {
916  throw std::out_of_range("column not found: " + pColumnName);
917  }
918 
919  return GetCell<T>(columnIdx, pRowIdx);
920  }
921 
929  template<typename T>
930  T GetCell(const std::string& pColumnName, const size_t pRowIdx, ConvFunc<T> pToVal) const
931  {
932  const ssize_t columnIdx = GetColumnIdx(pColumnName);
933  if (columnIdx < 0)
934  {
935  throw std::out_of_range("column not found: " + pColumnName);
936  }
937 
938  return GetCell<T>(columnIdx, pRowIdx, pToVal);
939  }
940 
947  template<typename T>
948  T GetCell(const size_t pColumnIdx, const std::string& pRowName) const
949  {
950  const ssize_t rowIdx = GetRowIdx(pRowName);
951  if (rowIdx < 0)
952  {
953  throw std::out_of_range("row not found: " + pRowName);
954  }
955 
956  return GetCell<T>(pColumnIdx, rowIdx);
957  }
958 
966  template<typename T>
967  T GetCell(const size_t pColumnIdx, const std::string& pRowName, ConvFunc<T> pToVal) const
968  {
969  const ssize_t rowIdx = GetRowIdx(pRowName);
970  if (rowIdx < 0)
971  {
972  throw std::out_of_range("row not found: " + pRowName);
973  }
974 
975  return GetCell<T>(pColumnIdx, rowIdx, pToVal);
976  }
977 
984  template<typename T>
985  void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T& pCell)
986  {
987  const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
988  const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
989 
990  while ((rowIdx + 1) > GetDataRowCount())
991  {
992  std::vector<std::string> row;
993  row.resize(GetDataColumnCount());
994  mData.push_back(row);
995  }
996 
997  if ((columnIdx + 1) > GetDataColumnCount())
998  {
999  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
1000  {
1001  itRow->resize(columnIdx + 1);
1002  }
1003  }
1004 
1005  std::string str;
1006  Converter<T> converter(mConverterParams);
1007  converter.ToStr(pCell, str);
1008  mData.at(rowIdx).at(columnIdx) = str;
1009  }
1010 
1017  template<typename T>
1018  void SetCell(const std::string& pColumnName, const std::string& pRowName, const T& pCell)
1019  {
1020  const ssize_t columnIdx = GetColumnIdx(pColumnName);
1021  if (columnIdx < 0)
1022  {
1023  throw std::out_of_range("column not found: " + pColumnName);
1024  }
1025 
1026  const ssize_t rowIdx = GetRowIdx(pRowName);
1027  if (rowIdx < 0)
1028  {
1029  throw std::out_of_range("row not found: " + pRowName);
1030  }
1031 
1032  SetCell<T>(columnIdx, rowIdx, pCell);
1033  }
1034 
1040  std::string GetColumnName(const ssize_t pColumnIdx)
1041  {
1042  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
1043  if (mLabelParams.mColumnNameIdx < 0)
1044  {
1045  throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx));
1046  }
1047 
1048  return mData.at(mLabelParams.mColumnNameIdx).at(columnIdx);
1049  }
1050 
1056  void SetColumnName(size_t pColumnIdx, const std::string& pColumnName)
1057  {
1058  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
1059  mColumnNames[pColumnName] = columnIdx;
1060  if (mLabelParams.mColumnNameIdx < 0)
1061  {
1062  throw std::out_of_range("column name row index < 0: " + std::to_string(mLabelParams.mColumnNameIdx));
1063  }
1064 
1065  // increase table size if necessary:
1066  const int rowIdx = mLabelParams.mColumnNameIdx;
1067  if (rowIdx >= (int) mData.size())
1068  {
1069  mData.resize(rowIdx + 1);
1070  }
1071  auto& row = mData[rowIdx];
1072  if (columnIdx >= (int) row.size())
1073  {
1074  row.resize(columnIdx + 1);
1075  }
1076 
1077  mData.at(mLabelParams.mColumnNameIdx).at(columnIdx) = pColumnName;
1078  }
1079 
1084  std::vector<std::string> GetColumnNames()
1085  {
1086  if (mLabelParams.mColumnNameIdx >= 0)
1087  {
1088  return std::vector<std::string>(mData.at(mLabelParams.mColumnNameIdx).begin() +
1089  (mLabelParams.mRowNameIdx + 1),
1090  mData.at(mLabelParams.mColumnNameIdx).end());
1091  }
1092 
1093  return std::vector<std::string>();
1094  }
1095 
1101  std::string GetRowName(const ssize_t pRowIdx)
1102  {
1103  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
1104  if (mLabelParams.mRowNameIdx < 0)
1105  {
1106  throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx));
1107  }
1108 
1109  return mData.at(rowIdx).at(mLabelParams.mRowNameIdx);
1110  }
1111 
1117  void SetRowName(size_t pRowIdx, const std::string& pRowName)
1118  {
1119  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
1120  mRowNames[pRowName] = rowIdx;
1121  if (mLabelParams.mRowNameIdx < 0)
1122  {
1123  throw std::out_of_range("row name column index < 0: " + std::to_string(mLabelParams.mRowNameIdx));
1124  }
1125 
1126  // increase table size if necessary:
1127  if (rowIdx >= (int) mData.size())
1128  {
1129  mData.resize(rowIdx + 1);
1130  }
1131  auto& row = mData[rowIdx];
1132  if (mLabelParams.mRowNameIdx >= (int) row.size())
1133  {
1134  row.resize(mLabelParams.mRowNameIdx + 1);
1135  }
1136 
1137  mData.at(rowIdx).at(mLabelParams.mRowNameIdx) = pRowName;
1138  }
1139 
1144  std::vector<std::string> GetRowNames()
1145  {
1146  std::vector<std::string> rownames;
1147  if (mLabelParams.mRowNameIdx >= 0)
1148  {
1149  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
1150  {
1151  if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx)
1152  {
1153  rownames.push_back(itRow->at(mLabelParams.mRowNameIdx));
1154  }
1155  }
1156  }
1157  return rownames;
1158  }
1159 
1160  private:
1161  void ReadCsv()
1162  {
1163  std::ifstream stream;
1164  stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
1165  stream.open(mPath, std::ios::binary);
1166  ReadCsv(stream);
1167  }
1168 
1169  void ReadCsv(std::istream& pStream)
1170  {
1171  pStream.seekg(0, std::ios::end);
1172  std::streamsize length = pStream.tellg();
1173  pStream.seekg(0, std::ios::beg);
1174 
1175 #ifdef HAS_CODECVT
1176  std::vector<char> bom2b(2, '\0');
1177  if (length >= 2)
1178  {
1179  pStream.read(bom2b.data(), 2);
1180  pStream.seekg(0, std::ios::beg);
1181  }
1182 
1183  static const std::vector<char> bomU16le = { '\xff', '\xfe' };
1184  static const std::vector<char> bomU16be = { '\xfe', '\xff' };
1185  if ((bom2b == bomU16le) || (bom2b == bomU16be))
1186  {
1187  mIsUtf16 = true;
1188  mIsLE = (bom2b == bomU16le);
1189 
1190  std::wifstream wstream;
1191  wstream.exceptions(std::wifstream::failbit | std::wifstream::badbit);
1192  wstream.open(mPath, std::ios::binary);
1193  if (mIsLE)
1194  {
1195  wstream.imbue(std::locale(wstream.getloc(),
1196  new std::codecvt_utf16<wchar_t, 0x10ffff,
1197  static_cast<std::codecvt_mode>(std::consume_header |
1198  std::little_endian)>));
1199  }
1200  else
1201  {
1202  wstream.imbue(std::locale(wstream.getloc(),
1203  new std::codecvt_utf16<wchar_t, 0x10ffff,
1204  std::consume_header>));
1205  }
1206  std::wstringstream wss;
1207  wss << wstream.rdbuf();
1208  std::string utf8 = ToString(wss.str());
1209  std::stringstream ss(utf8);
1210  ParseCsv(ss, utf8.size());
1211  }
1212  else
1213 #endif
1214  {
1215  // check for UTF-8 Byte order mark and skip it when found
1216  if (length >= 3)
1217  {
1218  std::vector<char> bom3b(3, '\0');
1219  pStream.read(bom3b.data(), 3);
1220  static const std::vector<char> bomU8 = { '\xef', '\xbb', '\xbf' };
1221  if (bom3b != bomU8)
1222  {
1223  // file does not start with a UTF-8 Byte order mark
1224  pStream.seekg(0, std::ios::beg);
1225  }
1226  else
1227  {
1228  // file did start with a UTF-8 Byte order mark, simply skip it
1229  length -= 3;
1230  }
1231  }
1232 
1233  ParseCsv(pStream, length);
1234  }
1235  }
1236 
1237  void ParseCsv(std::istream& pStream, std::streamsize p_FileLength)
1238  {
1239  const std::streamsize bufLength = 64 * 1024;
1240  std::vector<char> buffer(bufLength);
1241  std::vector<std::string> row;
1242  std::string cell;
1243  bool quoted = false;
1244  int cr = 0;
1245  int lf = 0;
1246 
1247  while (p_FileLength > 0)
1248  {
1249  std::streamsize readLength = std::min(p_FileLength, bufLength);
1250  pStream.read(buffer.data(), readLength);
1251  for (int i = 0; i < readLength; ++i)
1252  {
1253  if (buffer[i] == '"')
1254  {
1255  if (cell.empty() || cell[0] == '"')
1256  {
1257  quoted = !quoted;
1258  }
1259  cell += buffer[i];
1260  }
1261  else if (buffer[i] == mSeparatorParams.mSeparator)
1262  {
1263  if (!quoted)
1264  {
1265  row.push_back(Unquote(Trim(cell)));
1266  cell.clear();
1267  }
1268  else
1269  {
1270  cell += buffer[i];
1271  }
1272  }
1273  else if (buffer[i] == '\r')
1274  {
1275  if (mSeparatorParams.mQuotedLinebreaks && quoted)
1276  {
1277  cell += buffer[i];
1278  }
1279  else
1280  {
1281  ++cr;
1282  }
1283  }
1284  else if (buffer[i] == '\n')
1285  {
1286  if (mSeparatorParams.mQuotedLinebreaks && quoted)
1287  {
1288  cell += buffer[i];
1289  }
1290  else
1291  {
1292  ++lf;
1293  row.push_back(Unquote(Trim(cell)));
1294  cell.clear();
1295  mData.push_back(row);
1296  row.clear();
1297  quoted = false;
1298  }
1299  }
1300  else
1301  {
1302  cell += buffer[i];
1303  }
1304  }
1305  p_FileLength -= readLength;
1306  }
1307 
1308  // Handle last line without linebreak
1309  if (!cell.empty() || !row.empty())
1310  {
1311  row.push_back(Unquote(Trim(cell)));
1312  cell.clear();
1313  mData.push_back(row);
1314  row.clear();
1315  }
1316 
1317  // Assume CR/LF if at least half the linebreaks have CR
1318  mSeparatorParams.mHasCR = (cr > (lf / 2));
1319 
1320  // Set up column labels
1321  if ((mLabelParams.mColumnNameIdx >= 0) &&
1322  (static_cast<ssize_t>(mData.size()) > mLabelParams.mColumnNameIdx))
1323  {
1324  int i = 0;
1325  for (auto& columnName : mData[mLabelParams.mColumnNameIdx])
1326  {
1327  mColumnNames[columnName] = i++;
1328  }
1329  }
1330 
1331  // Set up row labels
1332  if ((mLabelParams.mRowNameIdx >= 0) &&
1333  (static_cast<ssize_t>(mData.size()) >
1334  (mLabelParams.mColumnNameIdx + 1)))
1335  {
1336  int i = 0;
1337  for (auto& dataRow : mData)
1338  {
1339  if (static_cast<ssize_t>(dataRow.size()) > mLabelParams.mRowNameIdx)
1340  {
1341  mRowNames[dataRow[mLabelParams.mRowNameIdx]] = i++;
1342  }
1343  }
1344  }
1345  }
1346 
1347  void WriteCsv() const
1348  {
1349 #ifdef HAS_CODECVT
1350  if (mIsUtf16)
1351  {
1352  std::stringstream ss;
1353  WriteCsv(ss);
1354  std::string utf8 = ss.str();
1355  std::wstring wstr = ToWString(utf8);
1356 
1357  std::wofstream wstream;
1358  wstream.exceptions(std::wofstream::failbit | std::wofstream::badbit);
1359  wstream.open(mPath, std::ios::binary | std::ios::trunc);
1360 
1361  if (mIsLE)
1362  {
1363  wstream.imbue(std::locale(wstream.getloc(),
1364  new std::codecvt_utf16<wchar_t, 0x10ffff,
1365  static_cast<std::codecvt_mode>(std::little_endian)>));
1366  }
1367  else
1368  {
1369  wstream.imbue(std::locale(wstream.getloc(),
1370  new std::codecvt_utf16<wchar_t, 0x10ffff>));
1371  }
1372 
1373  wstream << (wchar_t) 0xfeff;
1374  wstream << wstr;
1375  }
1376  else
1377 #endif
1378  {
1379  std::ofstream stream;
1380  stream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
1381  stream.open(mPath, std::ios::binary | std::ios::trunc);
1382  WriteCsv(stream);
1383  }
1384  }
1385 
1386  void WriteCsv(std::ostream& pStream) const
1387  {
1388  for (auto itr = mData.begin(); itr != mData.end(); ++itr)
1389  {
1390  for (auto itc = itr->begin(); itc != itr->end(); ++itc)
1391  {
1392  if (mSeparatorParams.mAutoQuote &&
1393  ((itc->find(mSeparatorParams.mSeparator) != std::string::npos) ||
1394  (itc->find(' ') != std::string::npos)))
1395  {
1396  // escape quotes in string
1397  std::string str = *itc;
1398  ReplaceString(str, "\"", "\"\"");
1399 
1400  pStream << "\"" << str << "\"";
1401  }
1402  else
1403  {
1404  pStream << *itc;
1405  }
1406 
1407  if (std::distance(itc, itr->end()) > 1)
1408  {
1409  pStream << mSeparatorParams.mSeparator;
1410  }
1411  }
1412  pStream << (mSeparatorParams.mHasCR ? "\r\n" : "\n");
1413  }
1414  }
1415 
1416  size_t GetDataRowCount() const
1417  {
1418  return mData.size();
1419  }
1420 
1421  size_t GetDataColumnCount() const
1422  {
1423  return (mData.size() > 0) ? mData.at(0).size() : 0;
1424  }
1425 
1426  std::string Trim(const std::string& pStr)
1427  {
1428  if (mSeparatorParams.mTrim)
1429  {
1430  std::string str = pStr;
1431 
1432  // ltrim
1433  str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](int ch) { return !isspace(ch); }));
1434 
1435  // rtrim
1436  str.erase(std::find_if(str.rbegin(), str.rend(), [](int ch) { return !isspace(ch); }).base(), str.end());
1437 
1438  return str;
1439  }
1440  else
1441  {
1442  return pStr;
1443  }
1444  }
1445 
1446  std::string Unquote(const std::string& pStr)
1447  {
1448  if (mSeparatorParams.mAutoQuote && (pStr.size() >= 2) && (pStr.front() == '"') && (pStr.back() == '"'))
1449  {
1450  // remove start/end quotes
1451  std::string str = pStr.substr(1, pStr.size() - 2);
1452 
1453  // unescape quotes in string
1454  ReplaceString(str, "\"\"", "\"");
1455 
1456  return str;
1457  }
1458  else
1459  {
1460  return pStr;
1461  }
1462  }
1463 
1464 #ifdef HAS_CODECVT
1465 #if defined(_MSC_VER)
1466 #pragma warning (disable: 4996)
1467 #endif
1468  static std::string ToString(const std::wstring& pWStr)
1469  {
1470  size_t len = std::wcstombs(nullptr, pWStr.c_str(), 0) + 1;
1471  char* cstr = new char[len];
1472  std::wcstombs(cstr, pWStr.c_str(), len);
1473  std::string str(cstr);
1474  delete[] cstr;
1475  return str;
1476  }
1477 
1478  static std::wstring ToWString(const std::string& pStr)
1479  {
1480  size_t len = 1 + mbstowcs(nullptr, pStr.c_str(), 0);
1481  wchar_t* wcstr = new wchar_t[len];
1482  std::mbstowcs(wcstr, pStr.c_str(), len);
1483  std::wstring wstr(wcstr);
1484  delete[] wcstr;
1485  return wstr;
1486  }
1487 #if defined(_MSC_VER)
1488 #pragma warning (default: 4996)
1489 #endif
1490 #endif
1491 
1492  static void ReplaceString(std::string& pStr, const std::string& pSearch, const std::string& pReplace)
1493  {
1494  size_t pos = 0;
1495 
1496  while ((pos = pStr.find(pSearch, pos)) != std::string::npos)
1497  {
1498  pStr.replace(pos, pSearch.size(), pReplace);
1499  pos += pReplace.size();
1500  }
1501  }
1502 
1503  private:
1504  std::string mPath;
1505  LabelParams mLabelParams;
1506  SeparatorParams mSeparatorParams;
1507  ConverterParams mConverterParams;
1508  std::vector<std::vector<std::string>> mData;
1509  std::map<std::string, size_t> mColumnNames;
1510  std::map<std::string, size_t> mRowNames;
1511 #ifdef HAS_CODECVT
1512  bool mIsUtf16 = false;
1513  bool mIsLE = false;
1514 #endif
1515  };
1516 }
rapidcsv::Converter::ToVal
void ToVal(const std::string &pStr, T &pVal) const
Converts string holding a numerical value to numerical datatype representation.
Definition: rapidcsv.hpp:152
rapidcsv::Document::GetColumnIdx
ssize_t GetColumnIdx(const std::string &pColumnName) const
Get column index by name.
Definition: rapidcsv.hpp:457
rapidcsv::Document::RemoveRow
void RemoveRow(const std::string &pRowName)
Remove row by name.
Definition: rapidcsv.hpp:798
rapidcsv::Document::GetCell
T GetCell(const std::string &pColumnName, const std::string &pRowName, ConvFunc< T > pToVal) const
Get cell by name.
Definition: rapidcsv.hpp:887
rapidcsv::Document::GetRowName
std::string GetRowName(const ssize_t pRowIdx)
Get row name.
Definition: rapidcsv.hpp:1101
rapidcsv::ConverterParams::ConverterParams
ConverterParams(const bool pHasDefaultConverter=false, const long double pDefaultFloat=std::numeric_limits< long double >::signaling_NaN(), const long long pDefaultInteger=0)
Constructor.
Definition: rapidcsv.hpp:58
rapidcsv::Document::RemoveColumn
void RemoveColumn(const std::string &pColumnName)
Remove column by name.
Definition: rapidcsv.hpp:615
rapidcsv::SeparatorParams::mSeparator
char mSeparator
specifies the column separator.
Definition: rapidcsv.hpp:334
rapidcsv::LabelParams::LabelParams
LabelParams(const int pColumnNameIdx=0, const int pRowNameIdx=-1)
Constructor.
Definition: rapidcsv.hpp:286
rapidcsv::Document::GetRow
std::vector< T > GetRow(const size_t pRowIdx, ConvFunc< T > pToVal) const
Get row by index.
Definition: rapidcsv.hpp:684
rapidcsv::Converter::Converter
Converter(const ConverterParams &pConverterParams)
Constructor.
Definition: rapidcsv.hpp:114
rapidcsv::Document::GetCell
T GetCell(const std::string &pColumnName, const size_t pRowIdx, ConvFunc< T > pToVal) const
Get cell by column name and row index.
Definition: rapidcsv.hpp:930
rapidcsv::Document::Document
Document(std::istream &pStream, const LabelParams &pLabelParams=LabelParams(), const SeparatorParams &pSeparatorParams=SeparatorParams(), const ConverterParams &pConverterParams=ConverterParams())
Constructor.
Definition: rapidcsv.hpp:395
rapidcsv::Document::SetColumn
void SetColumn(const size_t pColumnIdx, const std::vector< T > &pColumn)
Set column by index.
Definition: rapidcsv.hpp:554
rapidcsv::Document::SetCell
void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T &pCell)
Set cell by index.
Definition: rapidcsv.hpp:985
rapidcsv::Document::Load
void Load(std::istream &pStream)
Read Document data from stream.
Definition: rapidcsv.hpp:422
rapidcsv::Document::GetColumnNames
std::vector< std::string > GetColumnNames()
Get column names.
Definition: rapidcsv.hpp:1084
rapidcsv::Document::RemoveRow
void RemoveRow(const size_t pRowIdx)
Remove row by index.
Definition: rapidcsv.hpp:788
rapidcsv::Document::GetColumnCount
size_t GetColumnCount() const
Get number of data columns (excluding label columns).
Definition: rapidcsv.hpp:630
rapidcsv::no_converter
Exception thrown when attempting to access Document data in a datatype which is not supported by the ...
Definition: rapidcsv.hpp:89
rapidcsv::Document::GetColumn
std::vector< T > GetColumn(const std::string &pColumnName) const
Get column by name.
Definition: rapidcsv.hpp:521
rapidcsv::LabelParams
Datastructure holding parameters controlling which row and column should be treated as labels.
Definition: rapidcsv.hpp:276
rapidcsv::Document
Class representing a CSV document.
Definition: rapidcsv.hpp:361
rapidcsv::Document::Save
void Save(std::ostream &pStream)
Write Document data to stream.
Definition: rapidcsv.hpp:447
rapidcsv::SeparatorParams::SeparatorParams
SeparatorParams(const char pSeparator=',', const bool pTrim=false, const bool pHasCR=sPlatformHasCR, const bool pQuotedLinebreaks=false, const bool pAutoQuote=true)
Constructor.
Definition: rapidcsv.hpp:320
rapidcsv::Document::GetCell
T GetCell(const size_t pColumnIdx, const std::string &pRowName) const
Get cell by column index and row name.
Definition: rapidcsv.hpp:948
rapidcsv::LabelParams::mRowNameIdx
int mRowNameIdx
specifies the zero-based column index of the row labels.
Definition: rapidcsv.hpp:300
rapidcsv::Document::SetColumn
void SetColumn(const std::string &pColumnName, const std::vector< T > &pColumn)
Set column by name.
Definition: rapidcsv.hpp:588
rapidcsv::Document::GetCell
T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const
Get cell by index.
Definition: rapidcsv.hpp:826
rapidcsv::Document::GetColumn
std::vector< T > GetColumn(const size_t pColumnIdx, ConvFunc< T > pToVal) const
Get column by index.
Definition: rapidcsv.hpp:499
rapidcsv::LabelParams::mColumnNameIdx
int mColumnNameIdx
specifies the zero-based row index of the column labels.
Definition: rapidcsv.hpp:295
rapidcsv::Document::GetCell
T GetCell(const std::string &pColumnName, const size_t pRowIdx) const
Get cell by column name and row index.
Definition: rapidcsv.hpp:911
rapidcsv::Document::GetRow
std::vector< T > GetRow(const size_t pRowIdx) const
Get row by index.
Definition: rapidcsv.hpp:660
rapidcsv::Document::SetRow
void SetRow(const size_t pRowIdx, const std::vector< T > &pRow)
Set row by index.
Definition: rapidcsv.hpp:740
rapidcsv::Document::SetCell
void SetCell(const std::string &pColumnName, const std::string &pRowName, const T &pCell)
Set cell by name.
Definition: rapidcsv.hpp:1018
rapidcsv::Document::GetRow
std::vector< T > GetRow(const std::string &pRowName) const
Get row by name.
Definition: rapidcsv.hpp:707
rapidcsv::Document::GetColumn
std::vector< T > GetColumn(const std::string &pColumnName, ConvFunc< T > pToVal) const
Get column by name.
Definition: rapidcsv.hpp:538
rapidcsv::ConverterParams::mHasDefaultConverter
bool mHasDefaultConverter
specifies if conversion of non-numerical strings shall be converted to a default numerical value,...
Definition: rapidcsv.hpp:71
rapidcsv::Document::GetRowIdx
ssize_t GetRowIdx(const std::string &pRowName) const
Get row index by name.
Definition: rapidcsv.hpp:642
rapidcsv::Document::SetColumnName
void SetColumnName(size_t pColumnIdx, const std::string &pColumnName)
Set column name.
Definition: rapidcsv.hpp:1056
rapidcsv::SeparatorParams::mHasCR
bool mHasCR
specifies whether new documents should use CR/LF instead of LF.
Definition: rapidcsv.hpp:344
rapidcsv::Document::GetCell
T GetCell(const size_t pColumnIdx, const std::string &pRowName, ConvFunc< T > pToVal) const
Get cell by column index and row name.
Definition: rapidcsv.hpp:967
rapidcsv::SeparatorParams::mAutoQuote
bool mAutoQuote
specifies whether to automatically dequote cell data.
Definition: rapidcsv.hpp:354
rapidcsv::Document::Document
Document(const std::string &pPath=std::string(), const LabelParams &pLabelParams=LabelParams(), const SeparatorParams &pSeparatorParams=SeparatorParams(), const ConverterParams &pConverterParams=ConverterParams())
Constructor.
Definition: rapidcsv.hpp:372
rapidcsv::Document::Save
void Save(const std::string &pPath=std::string())
Write Document data to file.
Definition: rapidcsv.hpp:434
rapidcsv::SeparatorParams::mTrim
bool mTrim
specifies whether to trim leading and trailing spaces from cells read.
Definition: rapidcsv.hpp:339
rapidcsv::Document::Load
void Load(const std::string &pPath)
Read Document data from file.
Definition: rapidcsv.hpp:412
rapidcsv::Document::SetRow
void SetRow(const std::string &pRowName, const std::vector< T > &pRow)
Set row by name.
Definition: rapidcsv.hpp:774
rapidcsv::Document::GetCell
T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc< T > pToVal) const
Get cell by index.
Definition: rapidcsv.hpp:845
rapidcsv::SeparatorParams
Datastructure holding parameters controlling how the CSV data fields are separated.
Definition: rapidcsv.hpp:307
rapidcsv::Document::SetRowName
void SetRowName(size_t pRowIdx, const std::string &pRowName)
Set row name.
Definition: rapidcsv.hpp:1117
rapidcsv::Document::RemoveColumn
void RemoveColumn(const size_t pColumnIdx)
Remove column by index.
Definition: rapidcsv.hpp:602
rapidcsv::ConverterParams
Datastructure holding parameters controlling how invalid numbers (including empty strings) should be ...
Definition: rapidcsv.hpp:49
rapidcsv::Document::GetColumn
std::vector< T > GetColumn(const size_t pColumnIdx) const
Get column by index.
Definition: rapidcsv.hpp:475
rapidcsv::ConverterParams::mDefaultFloat
long double mDefaultFloat
floating-point default value to represent invalid numbers.
Definition: rapidcsv.hpp:76
rapidcsv::Document::GetCell
T GetCell(const std::string &pColumnName, const std::string &pRowName) const
Get cell by name.
Definition: rapidcsv.hpp:862
rapidcsv::Converter::ToStr
void ToStr(const T &pVal, std::string &pStr) const
Converts numerical value to string representation.
Definition: rapidcsv.hpp:124
rapidcsv::Document::GetRowNames
std::vector< std::string > GetRowNames()
Get row names.
Definition: rapidcsv.hpp:1144
rapidcsv::Document::GetRowCount
size_t GetRowCount() const
Get number of data rows (excluding label rows).
Definition: rapidcsv.hpp:813
rapidcsv::Document::GetColumnName
std::string GetColumnName(const ssize_t pColumnIdx)
Get column name.
Definition: rapidcsv.hpp:1040
rapidcsv::Document::GetRow
std::vector< T > GetRow(const std::string &pRowName, ConvFunc< T > pToVal) const
Get row by name.
Definition: rapidcsv.hpp:724
rapidcsv::ConverterParams::mDefaultInteger
long long mDefaultInteger
integer default value to represent invalid numbers.
Definition: rapidcsv.hpp:81
rapidcsv::Converter
Class providing conversion to/from numerical datatypes and strings. Only intended for rapidcsv intern...
Definition: rapidcsv.hpp:107
rapidcsv::SeparatorParams::mQuotedLinebreaks
bool mQuotedLinebreaks
specifies whether to allow line breaks in quoted text.
Definition: rapidcsv.hpp:349