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>
25typedef SSIZE_T ssize_t;
26#endif
27
28namespace rapidcsv
29{
30#if defined(_MSC_VER)
31static const bool sPlatformHasCR = true;
32#else
33static 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 */
80class 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 */
94template <typename T>
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 */
236template <>
237inline 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 */
248template <>
249inline void Converter<std::string>::ToVal(const std::string &pStr,
250 std::string &pVal) const
251{
252 pVal = pStr;
253}
254
255template <typename T>
256using 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 */
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
Class providing conversion to/from numerical datatypes and strings.
Definition rapidcsv.h:96
void ToStr(const T &pVal, std::string &pStr) const
Converts numerical value to string representation.
Definition rapidcsv.h:113
void ToVal(const std::string &pStr, T &pVal) const
Converts string holding a numerical value to numerical datatype representation.
Definition rapidcsv.h:137
Converter(const ConverterParams &pConverterParams)
Constructor.
Definition rapidcsv.h:103
Class representing a CSV document.
Definition rapidcsv.h:383
std::vector< T > GetRow(const std::string &pRowName, ConvFunc< T > pToVal) const
Get row by name.
Definition rapidcsv.h:842
std::string GetRowName(const ssize_t pRowIdx)
Get row name.
Definition rapidcsv.h:1273
ssize_t GetColumnIdx(const std::string &pColumnName) const
Get column index by name.
Definition rapidcsv.h:515
void SetRow(const std::string &pRowName, const std::vector< T > &pRow)
Set row by name.
Definition rapidcsv.h:893
std::vector< T > GetRow(const size_t pRowIdx, ConvFunc< T > pToVal) const
Get row by index.
Definition rapidcsv.h:801
T GetCell(const std::string &pColumnName, const std::string &pRowName) const
Get cell by name.
Definition rapidcsv.h:1027
std::vector< T > GetColumn(const size_t pColumnIdx, ConvFunc< T > pToVal) const
Get column by index.
Definition rapidcsv.h:557
void RemoveColumn(const size_t pColumnIdx)
Remove column by index.
Definition rapidcsv.h:663
T GetCell(const size_t pColumnIdx, const std::string &pRowName) const
Get cell by column index and row name.
Definition rapidcsv.h:1115
void Clear()
Clears loaded Document data.
Definition rapidcsv.h:499
std::vector< std::string > GetColumnNames()
Get column names.
Definition rapidcsv.h:1255
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
std::vector< T > GetColumn(const std::string &pColumnName) const
Get column by name.
Definition rapidcsv.h:579
void RemoveRow(const std::string &pRowName)
Remove row by name.
Definition rapidcsv.h:917
T GetCell(const size_t pColumnIdx, const size_t pRowIdx, ConvFunc< T > pToVal) const
Get cell by index.
Definition rapidcsv.h:1010
void SetColumnName(size_t pColumnIdx, const std::string &pColumnName)
Set column name.
Definition rapidcsv.h:1226
void SetRowName(size_t pRowIdx, const std::string &pRowName)
Set row name.
Definition rapidcsv.h:1290
T GetCell(const size_t pColumnIdx, const size_t pRowIdx) const
Get cell by index.
Definition rapidcsv.h:991
std::vector< T > GetRow(const std::string &pRowName) const
Get row by name.
Definition rapidcsv.h:825
void SetRow(const size_t pRowIdx, const std::vector< T > &pRow)
Set row by index.
Definition rapidcsv.h:858
std::vector< T > GetColumn(const std::string &pColumnName, ConvFunc< T > pToVal) const
Get column by name.
Definition rapidcsv.h:596
std::vector< T > GetColumn(const size_t pColumnIdx) const
Get column by index.
Definition rapidcsv.h:533
std::vector< std::string > GetRowNames()
Get row names.
Definition rapidcsv.h:1318
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
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
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
void SetCell(const size_t pColumnIdx, const size_t pRowIdx, const T &pCell)
Set cell by index.
Definition rapidcsv.h:1153
T GetCell(const std::string &pColumnName, const size_t pRowIdx) const
Get cell by column name and row index.
Definition rapidcsv.h:1077
void Save(const std::string &pPath=std::string())
Write Document data to file.
Definition rapidcsv.h:480
ssize_t GetRowIdx(const std::string &pRowName) const
Get row index by name.
Definition rapidcsv.h:758
T GetCell(const std::string &pColumnName, const std::string &pRowName, ConvFunc< T > pToVal) const
Get cell by name.
Definition rapidcsv.h:1052
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
void Save(std::ostream &pStream)
Write Document data to stream.
Definition rapidcsv.h:493
void SetColumn(const std::string &pColumnName, const std::vector< T > &pColumn)
Set column by name.
Definition rapidcsv.h:649
void SetCell(const std::string &pColumnName, const std::string &pRowName, const T &pCell)
Set cell by name.
Definition rapidcsv.h:1186
std::string GetColumnName(const ssize_t pColumnIdx)
Get column name.
Definition rapidcsv.h:1209
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
void RemoveColumn(const std::string &pColumnName)
Remove column by name.
Definition rapidcsv.h:676
void RemoveRow(const size_t pRowIdx)
Remove row by index.
Definition rapidcsv.h:907
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
void SetColumn(const size_t pColumnIdx, const std::vector< T > &pColumn)
Set column by index.
Definition rapidcsv.h:612
size_t GetColumnCount() const
Get number of data columns (excluding label columns).
Definition rapidcsv.h:745
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
size_t GetRowCount() const
Get number of data rows (excluding label rows).
Definition rapidcsv.h:977
std::vector< T > GetRow(const size_t pRowIdx) const
Get row by index.
Definition rapidcsv.h:776
Exception thrown when attempting to access Document data in a datatype which is not supported by the ...
Definition rapidcsv.h:81
Datastructure holding parameters controlling how invalid numbers (including empty strings) should be ...
Definition rapidcsv.h:41
long double mDefaultFloat
floating-point default value to represent invalid numbers.
Definition rapidcsv.h:68
long long mDefaultInteger
integer default value to represent invalid numbers.
Definition rapidcsv.h:73
bool mHasDefaultConverter
specifies if conversion of non-numerical strings shall be converted to a default numerical value,...
Definition rapidcsv.h:63
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
Datastructure holding parameters controlling which row and column should be treated as labels.
Definition rapidcsv.h:263
int mRowNameIdx
specifies the zero-based column index of the row labels.
Definition rapidcsv.h:286
LabelParams(const int pColumnNameIdx=0, const int pRowNameIdx=-1)
Constructor.
Definition rapidcsv.h:273
int mColumnNameIdx
specifies the zero-based row index of the column labels.
Definition rapidcsv.h:281
Datastructure holding parameters controlling how special line formats should be treated.
Definition rapidcsv.h:346
bool mSkipCommentLines
specifies whether to skip lines prefixed with mCommentPrefix.
Definition rapidcsv.h:366
char mCommentPrefix
specifies which prefix character to indicate a comment line.
Definition rapidcsv.h:371
bool mSkipEmptyLines
specifies whether to skip empty lines.
Definition rapidcsv.h:376
LineReaderParams(const bool pSkipCommentLines=false, const char pCommentPrefix='#', const bool pSkipEmptyLines=false)
Constructor.
Definition rapidcsv.h:355
Datastructure holding parameters controlling how the CSV data fields are separated.
Definition rapidcsv.h:293
bool mQuotedLinebreaks
specifies whether to allow line breaks in quoted text.
Definition rapidcsv.h:333
bool mTrim
specifies whether to trim leading and trailing spaces from cells read.
Definition rapidcsv.h:323
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
bool mHasCR
specifies whether new documents should use CR/LF instead of LF.
Definition rapidcsv.h:328
char mSeparator
specifies the column separator.
Definition rapidcsv.h:318
bool mAutoQuote
specifies whether to automatically dequote cell data.
Definition rapidcsv.h:338
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.