Kstars

rapidcsv.h
1 /*
2  SPDX-FileCopyrightText: 2017-2021 Kristofer Berggren <https://github.com/d99kris/rapidcsv>
3  SPDX-License-Identifier: BSD-3-Clause
4 */
5 
6 #pragma once
7 
8 #include <algorithm>
9 #include <cassert>
10 #include <cmath>
11 #ifdef HAS_CODECVT
12 #include <codecvt>
13 #endif
14 #include <fstream>
15 #include <functional>
16 #include <iostream>
17 #include <map>
18 #include <sstream>
19 #include <string>
20 #include <typeinfo>
21 #include <vector>
22 
23 #if defined(_MSC_VER)
24 #include <BaseTsd.h>
25 typedef SSIZE_T ssize_t;
26 #endif
27 
28 namespace rapidcsv
29 {
30 #if defined(_MSC_VER)
31 static const bool sPlatformHasCR = true;
32 #else
33 static const bool sPlatformHasCR = false;
34 #endif
35 
36 /**
37  * @brief Datastructure holding parameters controlling how invalid numbers (including
38  * empty strings) should be handled.
39  */
41 {
42  /**
43  * @brief Constructor
44  * @param pHasDefaultConverter specifies if conversion of non-numerical strings shall be
45  * converted to a default numerical value, instead of causing
46  * an exception to be thrown (default).
47  * @param pDefaultFloat floating-point default value to represent invalid numbers.
48  * @param pDefaultInteger integer default value to represent invalid numbers.
49  */
50  explicit ConverterParams(const bool pHasDefaultConverter = false,
51  const long double pDefaultFloat =
52  std::numeric_limits<long double>::signaling_NaN(),
53  const long long pDefaultInteger = 0)
54  : mHasDefaultConverter(pHasDefaultConverter), mDefaultFloat(pDefaultFloat),
55  mDefaultInteger(pDefaultInteger)
56  {
57  }
58 
59  /**
60  * @brief specifies if conversion of non-numerical strings shall be converted to a default
61  * numerical value, instead of causing an exception to be thrown (default).
62  */
64 
65  /**
66  * @brief floating-point default value to represent invalid numbers.
67  */
68  long double mDefaultFloat;
69 
70  /**
71  * @brief integer default value to represent invalid numbers.
72  */
73  long long mDefaultInteger;
74 };
75 
76 /**
77  * @brief Exception thrown when attempting to access Document data in a datatype which
78  * is not supported by the Converter class.
79  */
80 class no_converter : public std::exception
81 {
82  /**
83  * @brief Provides details about the exception
84  * @returns an explanatory string
85  */
86  virtual const char *what() const throw() override { return "unsupported conversion datatype"; }
87 };
88 
89 /**
90  * @brief Class providing conversion to/from numerical datatypes and strings. Only
91  * intended for rapidcsv internal usage, but exposed externally to allow
92  * specialization for custom datatype conversions.
93  */
94 template <typename T>
95 class Converter
96 {
97  public:
98  /**
99  * @brief Constructor
100  * @param pConverterParams specifies how conversion of non-numerical values to
101  * numerical datatype shall be handled.
102  */
103  Converter(const ConverterParams &pConverterParams)
104  : mConverterParams(pConverterParams)
105  {
106  }
107 
108  /**
109  * @brief Converts numerical value to string representation.
110  * @param pVal numerical value
111  * @param pStr output string
112  */
113  void ToStr(const T &pVal, std::string &pStr) const
114  {
115  if (typeid(T) == typeid(int) || typeid(T) == typeid(long) ||
116  typeid(T) == typeid(long long) || typeid(T) == typeid(unsigned) ||
117  typeid(T) == typeid(unsigned long) ||
118  typeid(T) == typeid(unsigned long long) || typeid(T) == typeid(float) ||
119  typeid(T) == typeid(double) || typeid(T) == typeid(long double) ||
120  typeid(T) == typeid(char))
121  {
122  std::ostringstream out;
123  out << pVal;
124  pStr = out.str();
125  }
126  else
127  {
128  throw no_converter();
129  }
130  }
131 
132  /**
133  * @brief Converts string holding a numerical value to numerical datatype representation.
134  * @param pVal numerical value
135  * @param pStr output string
136  */
137  void ToVal(const std::string &pStr, T &pVal) const
138  {
139  try
140  {
141  if (typeid(T) == typeid(int))
142  {
143  pVal = static_cast<T>(std::stoi(pStr));
144  return;
145  }
146  else if (typeid(T) == typeid(long))
147  {
148  pVal = static_cast<T>(std::stol(pStr));
149  return;
150  }
151  else if (typeid(T) == typeid(long long))
152  {
153  pVal = static_cast<T>(std::stoll(pStr));
154  return;
155  }
156  else if (typeid(T) == typeid(unsigned))
157  {
158  pVal = static_cast<T>(std::stoul(pStr));
159  return;
160  }
161  else if (typeid(T) == typeid(unsigned long))
162  {
163  pVal = static_cast<T>(std::stoul(pStr));
164  return;
165  }
166  else if (typeid(T) == typeid(unsigned long long))
167  {
168  pVal = static_cast<T>(std::stoull(pStr));
169  return;
170  }
171  }
172  catch (...)
173  {
174  if (!mConverterParams.mHasDefaultConverter)
175  {
176  throw;
177  }
178  else
179  {
180  pVal = static_cast<T>(mConverterParams.mDefaultInteger);
181  return;
182  }
183  }
184 
185  try
186  {
187  if (typeid(T) == typeid(float))
188  {
189  pVal = static_cast<T>(std::stof(pStr));
190  return;
191  }
192  else if (typeid(T) == typeid(double))
193  {
194  pVal = static_cast<T>(std::stod(pStr));
195  return;
196  }
197  else if (typeid(T) == typeid(long double))
198  {
199  pVal = static_cast<T>(std::stold(pStr));
200  return;
201  }
202  }
203  catch (...)
204  {
205  if (!mConverterParams.mHasDefaultConverter)
206  {
207  throw;
208  }
209  else
210  {
211  pVal = static_cast<T>(mConverterParams.mDefaultFloat);
212  return;
213  }
214  }
215 
216  if (typeid(T) == typeid(char))
217  {
218  pVal = static_cast<T>(pStr[0]);
219  return;
220  }
221  else
222  {
223  throw no_converter();
224  }
225  }
226 
227  private:
228  const ConverterParams &mConverterParams;
229 };
230 
231 /**
232  * @brief Specialized implementation handling string to string conversion.
233  * @param pVal string
234  * @param pStr string
235  */
236 template <>
237 inline void Converter<std::string>::ToStr(const std::string &pVal,
238  std::string &pStr) const
239 {
240  pStr = pVal;
241 }
242 
243 /**
244  * @brief Specialized implementation handling string to string conversion.
245  * @param pVal string
246  * @param pStr string
247  */
248 template <>
249 inline void Converter<std::string>::ToVal(const std::string &pStr,
250  std::string &pVal) const
251 {
252  pVal = pStr;
253 }
254 
255 template <typename T>
256 using ConvFunc = std::function<void(const std::string &pStr, T &pVal)>;
257 
258 /**
259  * @brief Datastructure holding parameters controlling which row and column should be
260  * treated as labels.
261  */
263 {
264  /**
265  * @brief Constructor
266  * @param pColumnNameIdx specifies the zero-based row index of the column labels, setting
267  * it to -1 prevents column lookup by label name, and gives access
268  * to all rows as document data. Default: 0
269  * @param pRowNameIdx specifies the zero-based column index of the row labels, setting
270  * it to -1 prevents row lookup by label name, and gives access
271  * to all columns as document data. Default: -1
272  */
273  explicit LabelParams(const int pColumnNameIdx = 0, const int pRowNameIdx = -1)
274  : mColumnNameIdx(pColumnNameIdx), mRowNameIdx(pRowNameIdx)
275  {
276  }
277 
278  /**
279  * @brief specifies the zero-based row index of the column labels.
280  */
282 
283  /**
284  * @brief specifies the zero-based column index of the row labels.
285  */
287 };
288 
289 /**
290  * @brief Datastructure holding parameters controlling how the CSV data fields are separated.
291  */
293 {
294  /**
295  * @brief Constructor
296  * @param pSeparator specifies the column separator (default ',').
297  * @param pTrim specifies whether to trim leading and trailing spaces from
298  * cells read (default false).
299  * @param pHasCR specifies whether a new document (i.e. not an existing document read)
300  * should use CR/LF instead of only LF (default is to use standard
301  * behavior of underlying platforms - CR/LF for Win, and LF for others).
302  * @param pQuotedLinebreaks specifies whether to allow line breaks in quoted text (default false)
303  * @param pAutoQuote specifies whether to automatically dequote data during read, and add
304  * quotes during write (default true).
305  */
306  explicit SeparatorParams(const char pSeparator = ',', const bool pTrim = false,
307  const bool pHasCR = sPlatformHasCR,
308  const bool pQuotedLinebreaks = false,
309  const bool pAutoQuote = true)
310  : mSeparator(pSeparator), mTrim(pTrim), mHasCR(pHasCR),
311  mQuotedLinebreaks(pQuotedLinebreaks), mAutoQuote(pAutoQuote)
312  {
313  }
314 
315  /**
316  * @brief specifies the column separator.
317  */
319 
320  /**
321  * @brief specifies whether to trim leading and trailing spaces from cells read.
322  */
323  bool mTrim;
324 
325  /**
326  * @brief specifies whether new documents should use CR/LF instead of LF.
327  */
328  bool mHasCR;
329 
330  /**
331  * @brief specifies whether to allow line breaks in quoted text.
332  */
334 
335  /**
336  * @brief specifies whether to automatically dequote cell data.
337  */
339 };
340 
341 /**
342  * @brief Datastructure holding parameters controlling how special line formats should be
343  * treated.
344  */
346 {
347  /**
348  * @brief Constructor
349  * @param pSkipCommentLines specifies whether to skip lines prefixed with
350  * mCommentPrefix. Default: false
351  * @param pCommentPrefix specifies which prefix character to indicate a comment
352  * line. Default: #
353  * @param pSkipEmptyLines specifies whether to skip empty lines. Default: false
354  */
355  explicit LineReaderParams(const bool pSkipCommentLines = false,
356  const char pCommentPrefix = '#',
357  const bool pSkipEmptyLines = false)
358  : mSkipCommentLines(pSkipCommentLines), mCommentPrefix(pCommentPrefix),
359  mSkipEmptyLines(pSkipEmptyLines)
360  {
361  }
362 
363  /**
364  * @brief specifies whether to skip lines prefixed with mCommentPrefix.
365  */
367 
368  /**
369  * @brief specifies which prefix character to indicate a comment line.
370  */
372 
373  /**
374  * @brief specifies whether to skip empty lines.
375  */
377 };
378 
379 /**
380  * @brief Class representing a CSV document.
381  */
382 class Document
383 {
384  public:
385  /**
386  * @brief Constructor
387  * @param pPath specifies the path of an existing CSV-file to populate the Document
388  * data with.
389  * @param pLabelParams specifies which row and column should be treated as labels.
390  * @param pSeparatorParams specifies which field and row separators should be used.
391  * @param pConverterParams specifies how invalid numbers (including empty strings) should be
392  * handled.
393  * @param pLineReaderParams specifies how special line formats should be treated.
394  */
395  explicit Document(const std::string &pPath = std::string(),
396  const LabelParams &pLabelParams = LabelParams(),
397  const SeparatorParams &pSeparatorParams = SeparatorParams(),
398  const ConverterParams &pConverterParams = ConverterParams(),
399  const LineReaderParams &pLineReaderParams = LineReaderParams())
400  : mPath(pPath), mLabelParams(pLabelParams), mSeparatorParams(pSeparatorParams),
401  mConverterParams(pConverterParams), mLineReaderParams(pLineReaderParams)
402  {
403  if (!mPath.empty())
404  {
405  ReadCsv();
406  }
407  }
408 
409  /**
410  * @brief Constructor
411  * @param pStream specifies an input stream to read CSV data from.
412  * @param pLabelParams specifies which row and column should be treated as labels.
413  * @param pSeparatorParams specifies which field and row separators should be used.
414  * @param pConverterParams specifies how invalid numbers (including empty strings) should be
415  * handled.
416  * @param pLineReaderParams specifies how special line formats should be treated.
417  */
418  explicit Document(std::istream &pStream,
419  const LabelParams &pLabelParams = LabelParams(),
420  const SeparatorParams &pSeparatorParams = SeparatorParams(),
421  const ConverterParams &pConverterParams = ConverterParams(),
422  const LineReaderParams &pLineReaderParams = LineReaderParams())
423  : mPath(), mLabelParams(pLabelParams), mSeparatorParams(pSeparatorParams),
424  mConverterParams(pConverterParams), mLineReaderParams(pLineReaderParams)
425  {
426  ReadCsv(pStream);
427  }
428 
429  /**
430  * @brief Read Document data from file.
431  * @param pPath specifies the path of an existing CSV-file to populate the Document
432  * data with.
433  * @param pLabelParams specifies which row and column should be treated as labels.
434  * @param pSeparatorParams specifies which field and row separators should be used.
435  * @param pConverterParams specifies how invalid numbers (including empty strings) should be
436  * handled.
437  * @param pLineReaderParams specifies how special line formats should be treated.
438  */
439  void Load(const std::string &pPath, const LabelParams &pLabelParams = LabelParams(),
440  const SeparatorParams &pSeparatorParams = SeparatorParams(),
441  const ConverterParams &pConverterParams = ConverterParams(),
442  const LineReaderParams &pLineReaderParams = LineReaderParams())
443  {
444  mPath = pPath;
445  mLabelParams = pLabelParams;
446  mSeparatorParams = pSeparatorParams;
447  mConverterParams = pConverterParams;
448  mLineReaderParams = pLineReaderParams;
449  ReadCsv();
450  }
451 
452  /**
453  * @brief Read Document data from stream.
454  * @param pStream specifies an input stream to read CSV data from.
455  * @param pLabelParams specifies which row and column should be treated as labels.
456  * @param pSeparatorParams specifies which field and row separators should be used.
457  * @param pConverterParams specifies how invalid numbers (including empty strings) should be
458  * handled.
459  * @param pLineReaderParams specifies how special line formats should be treated.
460  */
461  void Load(std::istream &pStream, const LabelParams &pLabelParams = LabelParams(),
462  const SeparatorParams &pSeparatorParams = SeparatorParams(),
463  const ConverterParams &pConverterParams = ConverterParams(),
464  const LineReaderParams &pLineReaderParams = LineReaderParams())
465  {
466  mPath = "";
467  mLabelParams = pLabelParams;
468  mSeparatorParams = pSeparatorParams;
469  mConverterParams = pConverterParams;
470  mLineReaderParams = pLineReaderParams;
471  ReadCsv(pStream);
472  }
473 
474  /**
475  * @brief Write Document data to file.
476  * @param pPath optionally specifies the path where the CSV-file will be created
477  * (if not specified, the original path provided when creating or
478  * loading the Document data will be used).
479  */
480  void Save(const std::string &pPath = std::string())
481  {
482  if (!pPath.empty())
483  {
484  mPath = pPath;
485  }
486  WriteCsv();
487  }
488 
489  /**
490  * @brief Write Document data to stream.
491  * @param pStream specifies an output stream to write the data to.
492  */
493  void Save(std::ostream &pStream) { WriteCsv(pStream); }
494 
495  /**
496  * @brief Clears loaded Document data.
497  *
498  */
499  void Clear()
500  {
501  mData.clear();
502  mColumnNames.clear();
503  mRowNames.clear();
504 #ifdef HAS_CODECVT
505  mIsUtf16 = false;
506  mIsLE = false;
507 #endif
508  }
509 
510  /**
511  * @brief Get column index by name.
512  * @param pColumnName column label name.
513  * @returns zero-based column index.
514  */
515  ssize_t GetColumnIdx(const std::string &pColumnName) const
516  {
517  if (mLabelParams.mColumnNameIdx >= 0)
518  {
519  if (mColumnNames.find(pColumnName) != mColumnNames.end())
520  {
521  return mColumnNames.at(pColumnName) - (mLabelParams.mRowNameIdx + 1);
522  }
523  }
524  return -1;
525  }
526 
527  /**
528  * @brief Get column by index.
529  * @param pColumnIdx zero-based column index.
530  * @returns vector of column data.
531  */
532  template <typename T>
533  std::vector<T> GetColumn(const size_t pColumnIdx) const
534  {
535  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
536  std::vector<T> column;
537  Converter<T> converter(mConverterParams);
538  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
539  {
540  if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx)
541  {
542  T val;
543  converter.ToVal(itRow->at(columnIdx), val);
544  column.push_back(val);
545  }
546  }
547  return column;
548  }
549 
550  /**
551  * @brief Get column by index.
552  * @param pColumnIdx zero-based column index.
553  * @param pToVal conversion function.
554  * @returns vector of column data.
555  */
556  template <typename T>
557  std::vector<T> GetColumn(const size_t pColumnIdx, ConvFunc<T> pToVal) const
558  {
559  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
560  std::vector<T> column;
561  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
562  {
563  if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx)
564  {
565  T val;
566  pToVal(itRow->at(columnIdx), val);
567  column.push_back(val);
568  }
569  }
570  return column;
571  }
572 
573  /**
574  * @brief Get column by name.
575  * @param pColumnName column label name.
576  * @returns vector of column data.
577  */
578  template <typename T>
579  std::vector<T> GetColumn(const std::string &pColumnName) const
580  {
581  const ssize_t columnIdx = GetColumnIdx(pColumnName);
582  if (columnIdx < 0)
583  {
584  throw std::out_of_range("column not found: " + pColumnName);
585  }
586  return GetColumn<T>(columnIdx);
587  }
588 
589  /**
590  * @brief Get column by name.
591  * @param pColumnName column label name.
592  * @param pToVal conversion function.
593  * @returns vector of column data.
594  */
595  template <typename T>
596  std::vector<T> GetColumn(const std::string &pColumnName, ConvFunc<T> pToVal) const
597  {
598  const ssize_t columnIdx = GetColumnIdx(pColumnName);
599  if (columnIdx < 0)
600  {
601  throw std::out_of_range("column not found: " + pColumnName);
602  }
603  return GetColumn<T>(columnIdx, pToVal);
604  }
605 
606  /**
607  * @brief Set column by index.
608  * @param pColumnIdx zero-based column index.
609  * @param pColumn vector of column data.
610  */
611  template <typename T>
612  void SetColumn(const size_t pColumnIdx, const std::vector<T> &pColumn)
613  {
614  const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
615 
616  while (pColumn.size() + (mLabelParams.mColumnNameIdx + 1) > GetDataRowCount())
617  {
618  std::vector<std::string> row;
619  row.resize(GetDataColumnCount());
620  mData.push_back(row);
621  }
622 
623  if ((columnIdx + 1) > GetDataColumnCount())
624  {
625  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
626  {
627  itRow->resize(columnIdx + 1 + (mLabelParams.mRowNameIdx + 1));
628  }
629  }
630 
631  Converter<T> converter(mConverterParams);
632  for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow)
633  {
634  std::string str;
635  converter.ToStr(*itRow, str);
636  mData
637  .at(std::distance(pColumn.begin(), itRow) +
638  (mLabelParams.mColumnNameIdx + 1))
639  .at(columnIdx) = str;
640  }
641  }
642 
643  /**
644  * @brief Set column by name.
645  * @param pColumnName column label name.
646  * @param pColumn vector of column data.
647  */
648  template <typename T>
649  void SetColumn(const std::string &pColumnName, const std::vector<T> &pColumn)
650  {
651  const ssize_t columnIdx = GetColumnIdx(pColumnName);
652  if (columnIdx < 0)
653  {
654  throw std::out_of_range("column not found: " + pColumnName);
655  }
656  SetColumn<T>(columnIdx, pColumn);
657  }
658 
659  /**
660  * @brief Remove column by index.
661  * @param pColumnIdx zero-based column index.
662  */
663  void RemoveColumn(const size_t pColumnIdx)
664  {
665  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
666  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
667  {
668  itRow->erase(itRow->begin() + columnIdx);
669  }
670  }
671 
672  /**
673  * @brief Remove column by name.
674  * @param pColumnName column label name.
675  */
676  void RemoveColumn(const std::string &pColumnName)
677  {
678  ssize_t columnIdx = GetColumnIdx(pColumnName);
679  if (columnIdx < 0)
680  {
681  throw std::out_of_range("column not found: " + pColumnName);
682  }
683 
684  RemoveColumn(columnIdx);
685  }
686 
687  /**
688  * @brief Insert column at specified index.
689  * @param pColumnIdx zero-based column index.
690  * @param pColumn vector of column data (optional argument).
691  * @param pColumnName column label name (optional argument).
692  */
693  template <typename T>
694  void InsertColumn(const size_t pColumnIdx,
695  const std::vector<T> &pColumn = std::vector<T>(),
696  const std::string &pColumnName = std::string())
697  {
698  const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
699 
700  std::vector<std::string> column;
701  if (pColumn.empty())
702  {
703  column.resize(GetDataRowCount());
704  }
705  else
706  {
707  column.resize(pColumn.size() + (mLabelParams.mColumnNameIdx + 1));
708  Converter<T> converter(mConverterParams);
709  for (auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow)
710  {
711  std::string str;
712  converter.ToStr(*itRow, str);
713  const size_t rowIdx = std::distance(pColumn.begin(), itRow) +
714  (mLabelParams.mColumnNameIdx + 1);
715  column.at(rowIdx) = str;
716  }
717  }
718 
719  while (column.size() > GetDataRowCount())
720  {
721  std::vector<std::string> row;
722  const size_t columnCount =
723  std::max(static_cast<size_t>(mLabelParams.mColumnNameIdx + 1),
724  GetDataColumnCount());
725  row.resize(columnCount);
726  mData.push_back(row);
727  }
728 
729  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
730  {
731  const size_t rowIdx = std::distance(mData.begin(), itRow);
732  itRow->insert(itRow->begin() + columnIdx, column.at(rowIdx));
733  }
734 
735  if (!pColumnName.empty())
736  {
737  SetColumnName(pColumnIdx, pColumnName);
738  }
739  }
740 
741  /**
742  * @brief Get number of data columns (excluding label columns).
743  * @returns column count.
744  */
745  size_t GetColumnCount() const
746  {
747  const ssize_t count =
748  static_cast<ssize_t>((mData.size() > 0) ? mData.at(0).size() : 0) -
749  (mLabelParams.mRowNameIdx + 1);
750  return (count >= 0) ? count : 0;
751  }
752 
753  /**
754  * @brief Get row index by name.
755  * @param pRowName row label name.
756  * @returns zero-based row index.
757  */
758  ssize_t GetRowIdx(const std::string &pRowName) const
759  {
760  if (mLabelParams.mRowNameIdx >= 0)
761  {
762  if (mRowNames.find(pRowName) != mRowNames.end())
763  {
764  return mRowNames.at(pRowName) - (mLabelParams.mColumnNameIdx + 1);
765  }
766  }
767  return -1;
768  }
769 
770  /**
771  * @brief Get row by index.
772  * @param pRowIdx zero-based row index.
773  * @returns vector of row data.
774  */
775  template <typename T>
776  std::vector<T> GetRow(const size_t pRowIdx) const
777  {
778  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
779  std::vector<T> row;
780  Converter<T> converter(mConverterParams);
781  for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end();
782  ++itCol)
783  {
784  if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx)
785  {
786  T val;
787  converter.ToVal(*itCol, val);
788  row.push_back(val);
789  }
790  }
791  return row;
792  }
793 
794  /**
795  * @brief Get row by index.
796  * @param pRowIdx zero-based row index.
797  * @param pToVal conversion function.
798  * @returns vector of row data.
799  */
800  template <typename T>
801  std::vector<T> GetRow(const size_t pRowIdx, ConvFunc<T> pToVal) const
802  {
803  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
804  std::vector<T> row;
805  Converter<T> converter(mConverterParams);
806  for (auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end();
807  ++itCol)
808  {
809  if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.mRowNameIdx)
810  {
811  T val;
812  pToVal(*itCol, val);
813  row.push_back(val);
814  }
815  }
816  return row;
817  }
818 
819  /**
820  * @brief Get row by name.
821  * @param pRowName row label name.
822  * @returns vector of row data.
823  */
824  template <typename T>
825  std::vector<T> GetRow(const std::string &pRowName) const
826  {
827  ssize_t rowIdx = GetRowIdx(pRowName);
828  if (rowIdx < 0)
829  {
830  throw std::out_of_range("row not found: " + pRowName);
831  }
832  return GetRow<T>(rowIdx);
833  }
834 
835  /**
836  * @brief Get row by name.
837  * @param pRowName row label name.
838  * @param pToVal conversion function.
839  * @returns vector of row data.
840  */
841  template <typename T>
842  std::vector<T> GetRow(const std::string &pRowName, ConvFunc<T> pToVal) const
843  {
844  ssize_t rowIdx = GetRowIdx(pRowName);
845  if (rowIdx < 0)
846  {
847  throw std::out_of_range("row not found: " + pRowName);
848  }
849  return GetRow<T>(rowIdx, pToVal);
850  }
851 
852  /**
853  * @brief Set row by index.
854  * @param pRowIdx zero-based row index.
855  * @param pRow vector of row data.
856  */
857  template <typename T>
858  void SetRow(const size_t pRowIdx, const std::vector<T> &pRow)
859  {
860  const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
861 
862  while ((rowIdx + 1) > GetDataRowCount())
863  {
864  std::vector<std::string> row;
865  row.resize(GetDataColumnCount());
866  mData.push_back(row);
867  }
868 
869  if (pRow.size() > GetDataColumnCount())
870  {
871  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
872  {
873  itRow->resize(pRow.size() + (mLabelParams.mRowNameIdx + 1));
874  }
875  }
876 
877  Converter<T> converter(mConverterParams);
878  for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol)
879  {
880  std::string str;
881  converter.ToStr(*itCol, str);
882  mData.at(rowIdx).at(std::distance(pRow.begin(), itCol) +
883  (mLabelParams.mRowNameIdx + 1)) = str;
884  }
885  }
886 
887  /**
888  * @brief Set row by name.
889  * @param pRowName row label name.
890  * @param pRow vector of row data.
891  */
892  template <typename T>
893  void SetRow(const std::string &pRowName, const std::vector<T> &pRow)
894  {
895  ssize_t rowIdx = GetRowIdx(pRowName);
896  if (rowIdx < 0)
897  {
898  throw std::out_of_range("row not found: " + pRowName);
899  }
900  return SetRow<T>(rowIdx, pRow);
901  }
902 
903  /**
904  * @brief Remove row by index.
905  * @param pRowIdx zero-based row index.
906  */
907  void RemoveRow(const size_t pRowIdx)
908  {
909  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
910  mData.erase(mData.begin() + rowIdx);
911  }
912 
913  /**
914  * @brief Remove row by name.
915  * @param pRowName row label name.
916  */
917  void RemoveRow(const std::string &pRowName)
918  {
919  ssize_t rowIdx = GetRowIdx(pRowName);
920  if (rowIdx < 0)
921  {
922  throw std::out_of_range("row not found: " + pRowName);
923  }
924 
925  RemoveRow(rowIdx);
926  }
927 
928  /**
929  * @brief Insert row at specified index.
930  * @param pRowIdx zero-based row index.
931  * @param pRow vector of row data (optional argument).
932  * @param pRowName row label name (optional argument).
933  */
934  template <typename T>
935  void InsertRow(const size_t pRowIdx, const std::vector<T> &pRow = std::vector<T>(),
936  const std::string &pRowName = std::string())
937  {
938  const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
939 
940  std::vector<std::string> row;
941  if (pRow.empty())
942  {
943  row.resize(GetDataColumnCount());
944  }
945  else
946  {
947  row.resize(pRow.size() + (mLabelParams.mRowNameIdx + 1));
948  Converter<T> converter(mConverterParams);
949  for (auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol)
950  {
951  std::string str;
952  converter.ToStr(*itCol, str);
953  row.at(std::distance(pRow.begin(), itCol) +
954  (mLabelParams.mRowNameIdx + 1)) = str;
955  }
956  }
957 
958  while (rowIdx > GetDataRowCount())
959  {
960  std::vector<std::string> tempRow;
961  tempRow.resize(GetDataColumnCount());
962  mData.push_back(tempRow);
963  }
964 
965  mData.insert(mData.begin() + rowIdx, row);
966 
967  if (!pRowName.empty())
968  {
969  SetRowName(pRowIdx, pRowName);
970  }
971  }
972 
973  /**
974  * @brief Get number of data rows (excluding label rows).
975  * @returns row count.
976  */
977  size_t GetRowCount() const
978  {
979  const ssize_t count =
980  static_cast<ssize_t>(mData.size()) - (mLabelParams.mColumnNameIdx + 1);
981  return (count >= 0) ? count : 0;
982  }
983 
984  /**
985  * @brief Get cell by index.
986  * @param pColumnIdx zero-based column index.
987  * @param pRowIdx zero-based row index.
988  * @returns cell data.
989  */
990  template <typename T>
991  T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const
992  {
993  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
994  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
995 
996  T val;
997  Converter<T> converter(mConverterParams);
998  converter.ToVal(mData.at(rowIdx).at(columnIdx), val);
999  return val;
1000  }
1001 
1002  /**
1003  * @brief Get cell by index.
1004  * @param pColumnIdx zero-based column index.
1005  * @param pRowIdx zero-based row index.
1006  * @param pToVal conversion function.
1007  * @returns cell data.
1008  */
1009  template <typename T>
1010  T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc<T> pToVal) const
1011  {
1012  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
1013  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
1014 
1015  T val;
1016  pToVal(mData.at(rowIdx).at(columnIdx), val);
1017  return val;
1018  }
1019 
1020  /**
1021  * @brief Get cell by name.
1022  * @param pColumnName column label name.
1023  * @param pRowName row label name.
1024  * @returns cell data.
1025  */
1026  template <typename T>
1027  T GetCell(const std::string &pColumnName, const std::string &pRowName) const
1028  {
1029  const ssize_t columnIdx = GetColumnIdx(pColumnName);
1030  if (columnIdx < 0)
1031  {
1032  throw std::out_of_range("column not found: " + pColumnName);
1033  }
1034 
1035  const ssize_t rowIdx = GetRowIdx(pRowName);
1036  if (rowIdx < 0)
1037  {
1038  throw std::out_of_range("row not found: " + pRowName);
1039  }
1040 
1041  return GetCell<T>(columnIdx, rowIdx);
1042  }
1043 
1044  /**
1045  * @brief Get cell by name.
1046  * @param pColumnName column label name.
1047  * @param pRowName row label name.
1048  * @param pToVal conversion function.
1049  * @returns cell data.
1050  */
1051  template <typename T>
1052  T GetCell(const std::string &pColumnName, const std::string &pRowName,
1053  ConvFunc<T> pToVal) const
1054  {
1055  const ssize_t columnIdx = GetColumnIdx(pColumnName);
1056  if (columnIdx < 0)
1057  {
1058  throw std::out_of_range("column not found: " + pColumnName);
1059  }
1060 
1061  const ssize_t rowIdx = GetRowIdx(pRowName);
1062  if (rowIdx < 0)
1063  {
1064  throw std::out_of_range("row not found: " + pRowName);
1065  }
1066 
1067  return GetCell<T>(columnIdx, rowIdx, pToVal);
1068  }
1069 
1070  /**
1071  * @brief Get cell by column name and row index.
1072  * @param pColumnName column label name.
1073  * @param pRowIdx zero-based row index.
1074  * @returns cell data.
1075  */
1076  template <typename T>
1077  T GetCell(const std::string &pColumnName, const size_t pRowIdx) const
1078  {
1079  const ssize_t columnIdx = GetColumnIdx(pColumnName);
1080  if (columnIdx < 0)
1081  {
1082  throw std::out_of_range("column not found: " + pColumnName);
1083  }
1084 
1085  return GetCell<T>(columnIdx, pRowIdx);
1086  }
1087 
1088  /**
1089  * @brief Get cell by column name and row index.
1090  * @param pColumnName column label name.
1091  * @param pRowIdx zero-based row index.
1092  * @param pToVal conversion function.
1093  * @returns cell data.
1094  */
1095  template <typename T>
1096  T GetCell(const std::string &pColumnName, const size_t pRowIdx,
1097  ConvFunc<T> pToVal) const
1098  {
1099  const ssize_t columnIdx = GetColumnIdx(pColumnName);
1100  if (columnIdx < 0)
1101  {
1102  throw std::out_of_range("column not found: " + pColumnName);
1103  }
1104 
1105  return GetCell<T>(columnIdx, pRowIdx, pToVal);
1106  }
1107 
1108  /**
1109  * @brief Get cell by column index and row name.
1110  * @param pColumnIdx zero-based column index.
1111  * @param pRowName row label name.
1112  * @returns cell data.
1113  */
1114  template <typename T>
1115  T GetCell(const size_t pColumnIdx, const std::string &pRowName) const
1116  {
1117  const ssize_t rowIdx = GetRowIdx(pRowName);
1118  if (rowIdx < 0)
1119  {
1120  throw std::out_of_range("row not found: " + pRowName);
1121  }
1122 
1123  return GetCell<T>(pColumnIdx, rowIdx);
1124  }
1125 
1126  /**
1127  * @brief Get cell by column index and row name.
1128  * @param pColumnIdx zero-based column index.
1129  * @param pRowName row label name.
1130  * @param pToVal conversion function.
1131  * @returns cell data.
1132  */
1133  template <typename T>
1134  T GetCell(const size_t pColumnIdx, const std::string &pRowName,
1135  ConvFunc<T> pToVal) const
1136  {
1137  const ssize_t rowIdx = GetRowIdx(pRowName);
1138  if (rowIdx < 0)
1139  {
1140  throw std::out_of_range("row not found: " + pRowName);
1141  }
1142 
1143  return GetCell<T>(pColumnIdx, rowIdx, pToVal);
1144  }
1145 
1146  /**
1147  * @brief Set cell by index.
1148  * @param pRowIdx zero-based row index.
1149  * @param pColumnIdx zero-based column index.
1150  * @param pCell cell data.
1151  */
1152  template <typename T>
1153  void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T &pCell)
1154  {
1155  const size_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
1156  const size_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
1157 
1158  while ((rowIdx + 1) > GetDataRowCount())
1159  {
1160  std::vector<std::string> row;
1161  row.resize(GetDataColumnCount());
1162  mData.push_back(row);
1163  }
1164 
1165  if ((columnIdx + 1) > GetDataColumnCount())
1166  {
1167  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
1168  {
1169  itRow->resize(columnIdx + 1);
1170  }
1171  }
1172 
1173  std::string str;
1174  Converter<T> converter(mConverterParams);
1175  converter.ToStr(pCell, str);
1176  mData.at(rowIdx).at(columnIdx) = str;
1177  }
1178 
1179  /**
1180  * @brief Set cell by name.
1181  * @param pColumnName column label name.
1182  * @param pRowName row label name.
1183  * @param pCell cell data.
1184  */
1185  template <typename T>
1186  void SetCell(const std::string &pColumnName, const std::string &pRowName,
1187  const T &pCell)
1188  {
1189  const ssize_t columnIdx = GetColumnIdx(pColumnName);
1190  if (columnIdx < 0)
1191  {
1192  throw std::out_of_range("column not found: " + pColumnName);
1193  }
1194 
1195  const ssize_t rowIdx = GetRowIdx(pRowName);
1196  if (rowIdx < 0)
1197  {
1198  throw std::out_of_range("row not found: " + pRowName);
1199  }
1200 
1201  SetCell<T>(columnIdx, rowIdx, pCell);
1202  }
1203 
1204  /**
1205  * @brief Get column name
1206  * @param pColumnIdx zero-based column index.
1207  * @returns column name.
1208  */
1209  std::string GetColumnName(const ssize_t pColumnIdx)
1210  {
1211  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
1212  if (mLabelParams.mColumnNameIdx < 0)
1213  {
1214  throw std::out_of_range("column name row index < 0: " +
1215  std::to_string(mLabelParams.mColumnNameIdx));
1216  }
1217 
1218  return mData.at(mLabelParams.mColumnNameIdx).at(columnIdx);
1219  }
1220 
1221  /**
1222  * @brief Set column name
1223  * @param pColumnIdx zero-based column index.
1224  * @param pColumnName column name.
1225  */
1226  void SetColumnName(size_t pColumnIdx, const std::string &pColumnName)
1227  {
1228  const ssize_t columnIdx = pColumnIdx + (mLabelParams.mRowNameIdx + 1);
1229  mColumnNames[pColumnName] = columnIdx;
1230  if (mLabelParams.mColumnNameIdx < 0)
1231  {
1232  throw std::out_of_range("column name row index < 0: " +
1233  std::to_string(mLabelParams.mColumnNameIdx));
1234  }
1235 
1236  // increase table size if necessary:
1237  const int rowIdx = mLabelParams.mColumnNameIdx;
1238  if (rowIdx >= static_cast<int>(mData.size()))
1239  {
1240  mData.resize(rowIdx + 1);
1241  }
1242  auto &row = mData[rowIdx];
1243  if (columnIdx >= static_cast<int>(row.size()))
1244  {
1245  row.resize(columnIdx + 1);
1246  }
1247 
1248  mData.at(mLabelParams.mColumnNameIdx).at(columnIdx) = pColumnName;
1249  }
1250 
1251  /**
1252  * @brief Get column names
1253  * @returns vector of column names.
1254  */
1255  std::vector<std::string> GetColumnNames()
1256  {
1257  if (mLabelParams.mColumnNameIdx >= 0)
1258  {
1259  return std::vector<std::string>(
1260  mData.at(mLabelParams.mColumnNameIdx).begin() +
1261  (mLabelParams.mRowNameIdx + 1),
1262  mData.at(mLabelParams.mColumnNameIdx).end());
1263  }
1264 
1265  return std::vector<std::string>();
1266  }
1267 
1268  /**
1269  * @brief Get row name
1270  * @param pRowIdx zero-based column index.
1271  * @returns row name.
1272  */
1273  std::string GetRowName(const ssize_t pRowIdx)
1274  {
1275  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
1276  if (mLabelParams.mRowNameIdx < 0)
1277  {
1278  throw std::out_of_range("row name column index < 0: " +
1279  std::to_string(mLabelParams.mRowNameIdx));
1280  }
1281 
1282  return mData.at(rowIdx).at(mLabelParams.mRowNameIdx);
1283  }
1284 
1285  /**
1286  * @brief Set row name
1287  * @param pRowIdx zero-based row index.
1288  * @param pRowName row name.
1289  */
1290  void SetRowName(size_t pRowIdx, const std::string &pRowName)
1291  {
1292  const ssize_t rowIdx = pRowIdx + (mLabelParams.mColumnNameIdx + 1);
1293  mRowNames[pRowName] = rowIdx;
1294  if (mLabelParams.mRowNameIdx < 0)
1295  {
1296  throw std::out_of_range("row name column index < 0: " +
1297  std::to_string(mLabelParams.mRowNameIdx));
1298  }
1299 
1300  // increase table size if necessary:
1301  if (rowIdx >= static_cast<int>(mData.size()))
1302  {
1303  mData.resize(rowIdx + 1);
1304  }
1305  auto &row = mData[rowIdx];
1306  if (mLabelParams.mRowNameIdx >= static_cast<int>(row.size()))
1307  {
1308  row.resize(mLabelParams.mRowNameIdx + 1);
1309  }
1310 
1311  mData.at(rowIdx).at(mLabelParams.mRowNameIdx) = pRowName;
1312  }
1313 
1314  /**
1315  * @brief Get row names
1316  * @returns vector of row names.
1317  */
1318  std::vector<std::string> GetRowNames()
1319  {
1320  std::vector<std::string> rownames;
1321  if (mLabelParams.mRowNameIdx >= 0)
1322  {
1323  for (auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
1324  {
1325  if (std::distance(mData.begin(), itRow) > mLabelParams.mColumnNameIdx)
1326  {
1327  rownames.push_back(itRow->at(mLabelParams.mRowNameIdx));
1328  }
1329  }
1330  }
1331  return rownames;
1332  }
1333 
1334  private:
1335  void ReadCsv()
1336  {
1337  std::ifstream stream;
1338  stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
1339  stream.open(mPath, std::ios::binary);
1340  ReadCsv(stream);
1341  }
1342 
1343  void ReadCsv(std::istream &pStream)
1344  {
1345  Clear();
1346  pStream.seekg(0, std::ios::end);
1347  std::streamsize length = pStream.tellg();
1348  pStream.seekg(0, std::ios::beg);
1349 
1350 #ifdef HAS_CODECVT
1351  std::vector<char> bom2b(2, '\0');
1352  if (length >= 2)
1353  {
1354  pStream.read(bom2b.data(), 2);
1355  pStream.seekg(0, std::ios::beg);
1356  }
1357 
1358  static const std::vector<char> bomU16le = { '\xff', '\xfe' };
1359  static const std::vector<char> bomU16be = { '\xfe', '\xff' };
1360  if ((bom2b == bomU16le) || (bom2b == bomU16be))
1361  {
1362  mIsUtf16 = true;
1363  mIsLE = (bom2b == bomU16le);
1364 
1365  std::wifstream wstream;
1366  wstream.exceptions(std::wifstream::failbit | std::wifstream::badbit);
1367  wstream.open(mPath, std::ios::binary);
1368  if (mIsLE)
1369  {
1370  wstream.imbue(
1371  std::locale(wstream.getloc(),
1372  new std::codecvt_utf16<wchar_t, 0x10ffff,
1373  static_cast<std::codecvt_mode>(
1374  std::consume_header |
1375  std::little_endian)>));
1376  }
1377  else
1378  {
1379  wstream.imbue(std::locale(
1380  wstream.getloc(),
1381  new std::codecvt_utf16<wchar_t, 0x10ffff, std::consume_header>));
1382  }
1383  std::wstringstream wss;
1384  wss << wstream.rdbuf();
1385  std::string utf8 = ToString(wss.str());
1386  std::stringstream ss(utf8);
1387  ParseCsv(ss, utf8.size());
1388  }
1389  else
1390 #endif
1391  {
1392  // check for UTF-8 Byte order mark and skip it when found
1393  if (length >= 3)
1394  {
1395  std::vector<char> bom3b(3, '\0');
1396  pStream.read(bom3b.data(), 3);
1397  static const std::vector<char> bomU8 = { '\xef', '\xbb', '\xbf' };
1398  if (bom3b != bomU8)
1399  {
1400  // file does not start with a UTF-8 Byte order mark
1401  pStream.seekg(0, std::ios::beg);
1402  }
1403  else
1404  {
1405  // file did start with a UTF-8 Byte order mark, simply skip it
1406  length -= 3;
1407  }
1408  }
1409 
1410  ParseCsv(pStream, length);
1411  }
1412  }
1413 
1414  void ParseCsv(std::istream &pStream, std::streamsize p_FileLength)
1415  {
1416  const std::streamsize bufLength = 64 * 1024;
1417  std::vector<char> buffer(bufLength);
1418  std::vector<std::string> row;
1419  std::string cell;
1420  bool quoted = false;
1421  int cr = 0;
1422  int lf = 0;
1423 
1424  while (p_FileLength > 0)
1425  {
1426  std::streamsize readLength =
1427  std::min<std::streamsize>(p_FileLength, bufLength);
1428  pStream.read(buffer.data(), readLength);
1429  for (int i = 0; i < readLength; ++i)
1430  {
1431  if (buffer[i] == '"')
1432  {
1433  if (cell.empty() || cell[0] == '"')
1434  {
1435  quoted = !quoted;
1436  }
1437  cell += buffer[i];
1438  }
1439  else if (buffer[i] == mSeparatorParams.mSeparator)
1440  {
1441  if (!quoted)
1442  {
1443  row.push_back(Unquote(Trim(cell)));
1444  cell.clear();
1445  }
1446  else
1447  {
1448  cell += buffer[i];
1449  }
1450  }
1451  else if (buffer[i] == '\r')
1452  {
1453  if (mSeparatorParams.mQuotedLinebreaks && quoted)
1454  {
1455  cell += buffer[i];
1456  }
1457  else
1458  {
1459  ++cr;
1460  }
1461  }
1462  else if (buffer[i] == '\n')
1463  {
1464  if (mSeparatorParams.mQuotedLinebreaks && quoted)
1465  {
1466  cell += buffer[i];
1467  }
1468  else
1469  {
1470  ++lf;
1471  if (mLineReaderParams.mSkipEmptyLines && row.empty() &&
1472  cell.empty())
1473  {
1474  // skip empty line
1475  }
1476  else
1477  {
1478  row.push_back(Unquote(Trim(cell)));
1479 
1480  if (mLineReaderParams.mSkipCommentLines &&
1481  !row.at(0).empty() &&
1482  (row.at(0)[0] == mLineReaderParams.mCommentPrefix))
1483  {
1484  // skip comment line
1485  }
1486  else
1487  {
1488  mData.push_back(row);
1489  }
1490 
1491  cell.clear();
1492  row.clear();
1493  quoted = false;
1494  }
1495  }
1496  }
1497  else
1498  {
1499  cell += buffer[i];
1500  }
1501  }
1502  p_FileLength -= readLength;
1503  }
1504 
1505  // Handle last line without linebreak
1506  if (!cell.empty() || !row.empty())
1507  {
1508  row.push_back(Unquote(Trim(cell)));
1509  cell.clear();
1510  mData.push_back(row);
1511  row.clear();
1512  }
1513 
1514  // Assume CR/LF if at least half the linebreaks have CR
1515  mSeparatorParams.mHasCR = (cr > (lf / 2));
1516 
1517  // Set up column labels
1518  if ((mLabelParams.mColumnNameIdx >= 0) &&
1519  (static_cast<ssize_t>(mData.size()) > mLabelParams.mColumnNameIdx))
1520  {
1521  int i = 0;
1522  for (auto &columnName : mData[mLabelParams.mColumnNameIdx])
1523  {
1524  mColumnNames[columnName] = i++;
1525  }
1526  }
1527 
1528  // Set up row labels
1529  if ((mLabelParams.mRowNameIdx >= 0) &&
1530  (static_cast<ssize_t>(mData.size()) > (mLabelParams.mColumnNameIdx + 1)))
1531  {
1532  int i = 0;
1533  for (auto &dataRow : mData)
1534  {
1535  if (static_cast<ssize_t>(dataRow.size()) > mLabelParams.mRowNameIdx)
1536  {
1537  mRowNames[dataRow[mLabelParams.mRowNameIdx]] = i++;
1538  }
1539  }
1540  }
1541  }
1542 
1543  void WriteCsv() const
1544  {
1545 #ifdef HAS_CODECVT
1546  if (mIsUtf16)
1547  {
1548  std::stringstream ss;
1549  WriteCsv(ss);
1550  std::string utf8 = ss.str();
1551  std::wstring wstr = ToWString(utf8);
1552 
1553  std::wofstream wstream;
1554  wstream.exceptions(std::wofstream::failbit | std::wofstream::badbit);
1555  wstream.open(mPath, std::ios::binary | std::ios::trunc);
1556 
1557  if (mIsLE)
1558  {
1559  wstream.imbue(
1560  std::locale(wstream.getloc(),
1561  new std::codecvt_utf16<wchar_t, 0x10ffff,
1562  static_cast<std::codecvt_mode>(
1563  std::little_endian)>));
1564  }
1565  else
1566  {
1567  wstream.imbue(std::locale(wstream.getloc(),
1568  new std::codecvt_utf16<wchar_t, 0x10ffff>));
1569  }
1570 
1571  wstream << static_cast<wchar_t>(0xfeff);
1572  wstream << wstr;
1573  }
1574  else
1575 #endif
1576  {
1577  std::ofstream stream;
1578  stream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
1579  stream.open(mPath, std::ios::binary | std::ios::trunc);
1580  WriteCsv(stream);
1581  }
1582  }
1583 
1584  void WriteCsv(std::ostream &pStream) const
1585  {
1586  for (auto itr = mData.begin(); itr != mData.end(); ++itr)
1587  {
1588  for (auto itc = itr->begin(); itc != itr->end(); ++itc)
1589  {
1590  if (mSeparatorParams.mAutoQuote &&
1591  ((itc->find(mSeparatorParams.mSeparator) != std::string::npos) ||
1592  (itc->find(' ') != std::string::npos)))
1593  {
1594  // escape quotes in string
1595  std::string str = *itc;
1596  ReplaceString(str, "\"", "\"\"");
1597 
1598  pStream << "\"" << str << "\"";
1599  }
1600  else
1601  {
1602  pStream << *itc;
1603  }
1604 
1605  if (std::distance(itc, itr->end()) > 1)
1606  {
1607  pStream << mSeparatorParams.mSeparator;
1608  }
1609  }
1610  pStream << (mSeparatorParams.mHasCR ? "\r\n" : "\n");
1611  }
1612  }
1613 
1614  size_t GetDataRowCount() const { return mData.size(); }
1615 
1616  size_t GetDataColumnCount() const
1617  {
1618  return (mData.size() > 0) ? mData.at(0).size() : 0;
1619  }
1620 
1621  std::string Trim(const std::string &pStr)
1622  {
1623  if (mSeparatorParams.mTrim)
1624  {
1625  std::string str = pStr;
1626 
1627  // ltrim
1628  str.erase(str.begin(), std::find_if(str.begin(), str.end(),
1629  [](int ch) { return !isspace(ch); }));
1630 
1631  // rtrim
1632  str.erase(std::find_if(str.rbegin(), str.rend(),
1633  [](int ch) { return !isspace(ch); })
1634  .base(),
1635  str.end());
1636 
1637  return str;
1638  }
1639  else
1640  {
1641  return pStr;
1642  }
1643  }
1644 
1645  std::string Unquote(const std::string &pStr)
1646  {
1647  if (mSeparatorParams.mAutoQuote && (pStr.size() >= 2) && (pStr.front() == '"') &&
1648  (pStr.back() == '"'))
1649  {
1650  // remove start/end quotes
1651  std::string str = pStr.substr(1, pStr.size() - 2);
1652 
1653  // unescape quotes in string
1654  ReplaceString(str, "\"\"", "\"");
1655 
1656  return str;
1657  }
1658  else
1659  {
1660  return pStr;
1661  }
1662  }
1663 
1664 #ifdef HAS_CODECVT
1665 #if defined(_MSC_VER)
1666 #pragma warning(disable : 4996)
1667 #endif
1668  static std::string ToString(const std::wstring &pWStr)
1669  {
1670  size_t len = std::wcstombs(nullptr, pWStr.c_str(), 0) + 1;
1671  char *cstr = new char[len];
1672  std::wcstombs(cstr, pWStr.c_str(), len);
1673  std::string str(cstr);
1674  delete[] cstr;
1675  return str;
1676  }
1677 
1678  static std::wstring ToWString(const std::string &pStr)
1679  {
1680  size_t len = 1 + mbstowcs(nullptr, pStr.c_str(), 0);
1681  wchar_t *wcstr = new wchar_t[len];
1682  std::mbstowcs(wcstr, pStr.c_str(), len);
1683  std::wstring wstr(wcstr);
1684  delete[] wcstr;
1685  return wstr;
1686  }
1687 #if defined(_MSC_VER)
1688 #pragma warning(default : 4996)
1689 #endif
1690 #endif
1691 
1692  static void ReplaceString(std::string &pStr, const std::string &pSearch,
1693  const std::string &pReplace)
1694  {
1695  size_t pos = 0;
1696 
1697  while ((pos = pStr.find(pSearch, pos)) != std::string::npos)
1698  {
1699  pStr.replace(pos, pSearch.size(), pReplace);
1700  pos += pReplace.size();
1701  }
1702  }
1703 
1704  private:
1705  std::string mPath;
1706  LabelParams mLabelParams;
1707  SeparatorParams mSeparatorParams;
1708  ConverterParams mConverterParams;
1709  LineReaderParams mLineReaderParams;
1710  std::vector<std::vector<std::string>> mData;
1711  std::map<std::string, size_t> mColumnNames;
1712  std::map<std::string, size_t> mRowNames;
1713 #ifdef HAS_CODECVT
1714  bool mIsUtf16 = false;
1715  bool mIsLE = false;
1716 #endif
1717 };
1718 } // namespace rapidcsv
void ToVal(const std::string &pStr, T &pVal) const
Converts string holding a numerical value to numerical datatype representation.
Definition: rapidcsv.h:137
Document(std::istream &pStream, const LabelParams &pLabelParams=LabelParams(), const SeparatorParams &pSeparatorParams=SeparatorParams(), const ConverterParams &pConverterParams=ConverterParams(), const LineReaderParams &pLineReaderParams=LineReaderParams())
Constructor.
Definition: rapidcsv.h:418
ssize_t GetColumnIdx(const std::string &pColumnName) const
Get column index by name.
Definition: rapidcsv.h:515
void RemoveRow(const std::string &pRowName)
Remove row by name.
Definition: rapidcsv.h:917
T GetCell(const std::string &pColumnName, const std::string &pRowName, ConvFunc< T > pToVal) const
Get cell by name.
Definition: rapidcsv.h:1052
std::string GetRowName(const ssize_t pRowIdx)
Get row name.
Definition: rapidcsv.h:1273
ConverterParams(const bool pHasDefaultConverter=false, const long double pDefaultFloat=std::numeric_limits< long double >::signaling_NaN(), const long long pDefaultInteger=0)
Constructor.
Definition: rapidcsv.h:50
void RemoveColumn(const std::string &pColumnName)
Remove column by name.
Definition: rapidcsv.h:676
Datastructure holding parameters controlling how special line formats should be treated.
Definition: rapidcsv.h:345
char mSeparator
specifies the column separator.
Definition: rapidcsv.h:318
LabelParams(const int pColumnNameIdx=0, const int pRowNameIdx=-1)
Constructor.
Definition: rapidcsv.h:273
std::vector< T > GetRow(const size_t pRowIdx, ConvFunc< T > pToVal) const
Get row by index.
Definition: rapidcsv.h:801
void Load(std::istream &pStream, const LabelParams &pLabelParams=LabelParams(), const SeparatorParams &pSeparatorParams=SeparatorParams(), const ConverterParams &pConverterParams=ConverterParams(), const LineReaderParams &pLineReaderParams=LineReaderParams())
Read Document data from stream.
Definition: rapidcsv.h:461
Converter(const ConverterParams &pConverterParams)
Constructor.
Definition: rapidcsv.h:103
T GetCell(const std::string &pColumnName, const size_t pRowIdx, ConvFunc< T > pToVal) const
Get cell by column name and row index.
Definition: rapidcsv.h:1096
void SetColumn(const size_t pColumnIdx, const std::vector< T > &pColumn)
Set column by index.
Definition: rapidcsv.h:612
void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T &pCell)
Set cell by index.
Definition: rapidcsv.h:1153
std::vector< std::string > GetColumnNames()
Get column names.
Definition: rapidcsv.h:1255
void RemoveRow(const size_t pRowIdx)
Remove row by index.
Definition: rapidcsv.h:907
size_t GetColumnCount() const
Get number of data columns (excluding label columns).
Definition: rapidcsv.h:745
Exception thrown when attempting to access Document data in a datatype which is not supported by the ...
Definition: rapidcsv.h:80
std::vector< T > GetColumn(const std::string &pColumnName) const
Get column by name.
Definition: rapidcsv.h:579
Datastructure holding parameters controlling which row and column should be treated as labels.
Definition: rapidcsv.h:262
Class representing a CSV document.
Definition: rapidcsv.h:382
void Save(std::ostream &pStream)
Write Document data to stream.
Definition: rapidcsv.h:493
SeparatorParams(const char pSeparator=',', const bool pTrim=false, const bool pHasCR=sPlatformHasCR, const bool pQuotedLinebreaks=false, const bool pAutoQuote=true)
Constructor.
Definition: rapidcsv.h:306
T GetCell(const size_t pColumnIdx, const std::string &pRowName) const
Get cell by column index and row name.
Definition: rapidcsv.h:1115
int mRowNameIdx
specifies the zero-based column index of the row labels.
Definition: rapidcsv.h:286
void SetColumn(const std::string &pColumnName, const std::vector< T > &pColumn)
Set column by name.
Definition: rapidcsv.h:649
bool mSkipEmptyLines
specifies whether to skip empty lines.
Definition: rapidcsv.h:376
T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const
Get cell by index.
Definition: rapidcsv.h:991
std::vector< T > GetColumn(const size_t pColumnIdx, ConvFunc< T > pToVal) const
Get column by index.
Definition: rapidcsv.h:557
int mColumnNameIdx
specifies the zero-based row index of the column labels.
Definition: rapidcsv.h:281
Document(const std::string &pPath=std::string(), const LabelParams &pLabelParams=LabelParams(), const SeparatorParams &pSeparatorParams=SeparatorParams(), const ConverterParams &pConverterParams=ConverterParams(), const LineReaderParams &pLineReaderParams=LineReaderParams())
Constructor.
Definition: rapidcsv.h:395
char mCommentPrefix
specifies which prefix character to indicate a comment line.
Definition: rapidcsv.h:371
T GetCell(const std::string &pColumnName, const size_t pRowIdx) const
Get cell by column name and row index.
Definition: rapidcsv.h:1077
std::vector< T > GetRow(const size_t pRowIdx) const
Get row by index.
Definition: rapidcsv.h:776
void SetRow(const size_t pRowIdx, const std::vector< T > &pRow)
Set row by index.
Definition: rapidcsv.h:858
void SetCell(const std::string &pColumnName, const std::string &pRowName, const T &pCell)
Set cell by name.
Definition: rapidcsv.h:1186
std::vector< T > GetRow(const std::string &pRowName) const
Get row by name.
Definition: rapidcsv.h:825
std::vector< T > GetColumn(const std::string &pColumnName, ConvFunc< T > pToVal) const
Get column by name.
Definition: rapidcsv.h:596
bool mHasDefaultConverter
specifies if conversion of non-numerical strings shall be converted to a default numerical value,...
Definition: rapidcsv.h:63
ssize_t GetRowIdx(const std::string &pRowName) const
Get row index by name.
Definition: rapidcsv.h:758
void SetColumnName(size_t pColumnIdx, const std::string &pColumnName)
Set column name.
Definition: rapidcsv.h:1226
bool mHasCR
specifies whether new documents should use CR/LF instead of LF.
Definition: rapidcsv.h:328
void InsertColumn(const size_t pColumnIdx, const std::vector< T > &pColumn=std::vector< T >(), const std::string &pColumnName=std::string())
Insert column at specified index.
Definition: rapidcsv.h:694
T GetCell(const size_t pColumnIdx, const std::string &pRowName, ConvFunc< T > pToVal) const
Get cell by column index and row name.
Definition: rapidcsv.h:1134
bool mAutoQuote
specifies whether to automatically dequote cell data.
Definition: rapidcsv.h:338
void Save(const std::string &pPath=std::string())
Write Document data to file.
Definition: rapidcsv.h:480
bool mTrim
specifies whether to trim leading and trailing spaces from cells read.
Definition: rapidcsv.h:323
void Clear()
Clears loaded Document data.
Definition: rapidcsv.h:499
void SetRow(const std::string &pRowName, const std::vector< T > &pRow)
Set row by name.
Definition: rapidcsv.h:893
T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc< T > pToVal) const
Get cell by index.
Definition: rapidcsv.h:1010
Datastructure holding parameters controlling how the CSV data fields are separated.
Definition: rapidcsv.h:292
void SetRowName(size_t pRowIdx, const std::string &pRowName)
Set row name.
Definition: rapidcsv.h:1290
void RemoveColumn(const size_t pColumnIdx)
Remove column by index.
Definition: rapidcsv.h:663
Datastructure holding parameters controlling how invalid numbers (including empty strings) should be ...
Definition: rapidcsv.h:40
std::vector< T > GetColumn(const size_t pColumnIdx) const
Get column by index.
Definition: rapidcsv.h:533
void Load(const std::string &pPath, const LabelParams &pLabelParams=LabelParams(), const SeparatorParams &pSeparatorParams=SeparatorParams(), const ConverterParams &pConverterParams=ConverterParams(), const LineReaderParams &pLineReaderParams=LineReaderParams())
Read Document data from file.
Definition: rapidcsv.h:439
LineReaderParams(const bool pSkipCommentLines=false, const char pCommentPrefix='#', const bool pSkipEmptyLines=false)
Constructor.
Definition: rapidcsv.h:355
bool mSkipCommentLines
specifies whether to skip lines prefixed with mCommentPrefix.
Definition: rapidcsv.h:366
long double mDefaultFloat
floating-point default value to represent invalid numbers.
Definition: rapidcsv.h:68
T GetCell(const std::string &pColumnName, const std::string &pRowName) const
Get cell by name.
Definition: rapidcsv.h:1027
void ToStr(const T &pVal, std::string &pStr) const
Converts numerical value to string representation.
Definition: rapidcsv.h:113
std::vector< std::string > GetRowNames()
Get row names.
Definition: rapidcsv.h:1318
size_t GetRowCount() const
Get number of data rows (excluding label rows).
Definition: rapidcsv.h:977
std::string GetColumnName(const ssize_t pColumnIdx)
Get column name.
Definition: rapidcsv.h:1209
std::vector< T > GetRow(const std::string &pRowName, ConvFunc< T > pToVal) const
Get row by name.
Definition: rapidcsv.h:842
long long mDefaultInteger
integer default value to represent invalid numbers.
Definition: rapidcsv.h:73
void InsertRow(const size_t pRowIdx, const std::vector< T > &pRow=std::vector< T >(), const std::string &pRowName=std::string())
Insert row at specified index.
Definition: rapidcsv.h:935
Class providing conversion to/from numerical datatypes and strings.
Definition: rapidcsv.h:95
bool mQuotedLinebreaks
specifies whether to allow line breaks in quoted text.
Definition: rapidcsv.h:333
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jun 4 2023 03:57:16 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.