33 typedef SSIZE_T ssize_t;
39 static const bool sPlatformHasCR =
true;
41 static const bool sPlatformHasCR =
false;
59 const long double pDefaultFloat = std::numeric_limits<long double>::signaling_NaN(),
60 const long long pDefaultInteger = 0)
94 virtual const char* what()
const throw()
96 return "unsupported conversion datatype";
115 : mConverterParams(pConverterParams)
124 void ToStr(
const T& pVal, std::string& pStr)
const
126 if (
typeid(T) ==
typeid(
int) ||
127 typeid(T) ==
typeid(
long) ||
128 typeid(T) ==
typeid(
long long) ||
129 typeid(T) ==
typeid(
unsigned) ||
130 typeid(T) ==
typeid(
unsigned long) ||
131 typeid(T) ==
typeid(
unsigned long long) ||
132 typeid(T) ==
typeid(
float) ||
133 typeid(T) ==
typeid(
double) ||
134 typeid(T) ==
typeid(
long double) ||
135 typeid(T) ==
typeid(
char))
137 std::ostringstream out;
152 void ToVal(
const std::string& pStr, T& pVal)
const
156 if (
typeid(T) ==
typeid(
int))
158 pVal =
static_cast<T
>(std::stoi(pStr));
161 else if (
typeid(T) ==
typeid(long))
163 pVal =
static_cast<T
>(std::stol(pStr));
166 else if (
typeid(T) ==
typeid(
long long))
168 pVal =
static_cast<T
>(std::stoll(pStr));
171 else if (
typeid(T) ==
typeid(unsigned))
173 pVal =
static_cast<T
>(std::stoul(pStr));
176 else if (
typeid(T) ==
typeid(
unsigned long))
178 pVal =
static_cast<T
>(std::stoul(pStr));
181 else if (
typeid(T) ==
typeid(
unsigned long long))
183 pVal =
static_cast<T
>(std::stoull(pStr));
202 if (
typeid(T) ==
typeid(float))
204 pVal =
static_cast<T
>(std::stof(pStr));
207 else if (
typeid(T) ==
typeid(double))
209 pVal =
static_cast<T
>(std::stod(pStr));
212 else if (
typeid(T) ==
typeid(
long double))
214 pVal =
static_cast<T
>(std::stold(pStr));
231 if (
typeid(T) ==
typeid(char))
233 pVal =
static_cast<T
>(pStr[0]);
269 using ConvFunc = std::function<void (
const std::string & pStr, T & pVal)>;
286 explicit LabelParams(
const int pColumnNameIdx = 0,
const int pRowNameIdx = -1)
321 const bool pHasCR = sPlatformHasCR,
const bool pQuotedLinebreaks =
false,
322 const bool pAutoQuote =
true)
372 explicit Document(
const std::string& pPath = std::string(),
377 , mLabelParams(pLabelParams)
378 , mSeparatorParams(pSeparatorParams)
379 , mConverterParams(pConverterParams)
400 , mLabelParams(pLabelParams)
401 , mSeparatorParams(pSeparatorParams)
402 , mConverterParams(pConverterParams)
412 void Load(
const std::string& pPath)
422 void Load(std::istream& pStream)
434 void Save(
const std::string& pPath = std::string())
447 void Save(std::ostream& pStream)
461 if (mColumnNames.find(pColumnName) != mColumnNames.end())
463 return mColumnNames.at(pColumnName) - (mLabelParams.
mRowNameIdx + 1);
477 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
478 std::vector<T> column;
480 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
482 if (std::distance(mData.begin(), itRow) > mLabelParams.
mColumnNameIdx)
485 converter.
ToVal(itRow->at(columnIdx), val);
486 column.push_back(val);
499 std::vector<T>
GetColumn(
const size_t pColumnIdx, ConvFunc<T> pToVal)
const
501 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
502 std::vector<T> column;
503 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
505 if (std::distance(mData.begin(), itRow) > mLabelParams.
mColumnNameIdx)
508 pToVal(itRow->at(columnIdx), val);
509 column.push_back(val);
521 std::vector<T>
GetColumn(
const std::string& pColumnName)
const
526 throw std::out_of_range(
"column not found: " + pColumnName);
528 return GetColumn<T>(columnIdx);
538 std::vector<T>
GetColumn(
const std::string& pColumnName, ConvFunc<T> pToVal)
const
543 throw std::out_of_range(
"column not found: " + pColumnName);
545 return GetColumn<T>(columnIdx, pToVal);
554 void SetColumn(
const size_t pColumnIdx,
const std::vector<T>& pColumn)
556 const size_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
558 while (pColumn.size() + (mLabelParams.
mColumnNameIdx + 1) > GetDataRowCount())
560 std::vector<std::string> row;
561 row.resize(GetDataColumnCount());
562 mData.push_back(row);
565 if ((columnIdx + 1) > GetDataColumnCount())
567 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
569 itRow->resize(columnIdx + 1 + (mLabelParams.
mRowNameIdx + 1));
574 for (
auto itRow = pColumn.begin(); itRow != pColumn.end(); ++itRow)
577 converter.
ToStr(*itRow, str);
578 mData.at(std::distance(pColumn.begin(), itRow) + (mLabelParams.
mColumnNameIdx + 1)).at(columnIdx) = str;
588 void SetColumn(
const std::string& pColumnName,
const std::vector<T>& pColumn)
593 throw std::out_of_range(
"column not found: " + pColumnName);
595 SetColumn<T>(columnIdx, pColumn);
604 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
605 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
607 itRow->erase(itRow->begin() + columnIdx);
620 throw std::out_of_range(
"column not found: " + pColumnName);
632 const ssize_t count =
static_cast<ssize_t
>((mData.size() > 0) ? mData.at(0).size() : 0) -
634 return (count >= 0) ? count : 0;
646 if (mRowNames.find(pRowName) != mRowNames.end())
648 return mRowNames.at(pRowName) - (mLabelParams.
mColumnNameIdx + 1);
660 std::vector<T>
GetRow(
const size_t pRowIdx)
const
662 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
665 for (
auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol)
667 if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.
mRowNameIdx)
670 converter.
ToVal(*itCol, val);
684 std::vector<T>
GetRow(
const size_t pRowIdx, ConvFunc<T> pToVal)
const
686 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
689 for (
auto itCol = mData.at(rowIdx).begin(); itCol != mData.at(rowIdx).end(); ++itCol)
691 if (std::distance(mData.at(rowIdx).begin(), itCol) > mLabelParams.
mRowNameIdx)
707 std::vector<T>
GetRow(
const std::string& pRowName)
const
712 throw std::out_of_range(
"row not found: " + pRowName);
714 return GetRow<T>(rowIdx);
724 std::vector<T>
GetRow(
const std::string& pRowName, ConvFunc<T> pToVal)
const
729 throw std::out_of_range(
"row not found: " + pRowName);
731 return GetRow<T>(rowIdx, pToVal);
740 void SetRow(
const size_t pRowIdx,
const std::vector<T>& pRow)
742 const size_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
744 while ((rowIdx + 1) > GetDataRowCount())
746 std::vector<std::string> row;
747 row.resize(GetDataColumnCount());
748 mData.push_back(row);
751 if (pRow.size() > GetDataColumnCount())
753 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
755 itRow->resize(pRow.size() + (mLabelParams.
mRowNameIdx + 1));
760 for (
auto itCol = pRow.begin(); itCol != pRow.end(); ++itCol)
763 converter.
ToStr(*itCol, str);
764 mData.at(rowIdx).at(std::distance(pRow.begin(), itCol) + (mLabelParams.
mRowNameIdx + 1)) = str;
774 void SetRow(
const std::string& pRowName,
const std::vector<T>& pRow)
779 throw std::out_of_range(
"row not found: " + pRowName);
781 return SetRow<T>(rowIdx, pRow);
790 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
791 mData.erase(mData.begin() + rowIdx);
803 throw std::out_of_range(
"row not found: " + pRowName);
815 const ssize_t count =
static_cast<ssize_t
>(mData.size()) - (mLabelParams.
mColumnNameIdx + 1);
816 return (count >= 0) ? count : 0;
826 T
GetCell(
const size_t pColumnIdx,
const size_t pRowIdx)
const
828 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
829 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
833 converter.
ToVal(mData.at(rowIdx).at(columnIdx), val);
845 T
GetCell(
const size_t pColumnIdx,
const size_t pRowIdx, ConvFunc<T> pToVal)
const
847 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
848 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
851 pToVal(mData.at(rowIdx).at(columnIdx), val);
862 T
GetCell(
const std::string& pColumnName,
const std::string& pRowName)
const
867 throw std::out_of_range(
"column not found: " + pColumnName);
870 const ssize_t rowIdx =
GetRowIdx(pRowName);
873 throw std::out_of_range(
"row not found: " + pRowName);
876 return GetCell<T>(columnIdx, rowIdx);
887 T
GetCell(
const std::string& pColumnName,
const std::string& pRowName, ConvFunc<T> pToVal)
const
892 throw std::out_of_range(
"column not found: " + pColumnName);
895 const ssize_t rowIdx =
GetRowIdx(pRowName);
898 throw std::out_of_range(
"row not found: " + pRowName);
901 return GetCell<T>(columnIdx, rowIdx, pToVal);
911 T
GetCell(
const std::string& pColumnName,
const size_t pRowIdx)
const
916 throw std::out_of_range(
"column not found: " + pColumnName);
919 return GetCell<T>(columnIdx, pRowIdx);
930 T
GetCell(
const std::string& pColumnName,
const size_t pRowIdx, ConvFunc<T> pToVal)
const
935 throw std::out_of_range(
"column not found: " + pColumnName);
938 return GetCell<T>(columnIdx, pRowIdx, pToVal);
948 T
GetCell(
const size_t pColumnIdx,
const std::string& pRowName)
const
950 const ssize_t rowIdx =
GetRowIdx(pRowName);
953 throw std::out_of_range(
"row not found: " + pRowName);
956 return GetCell<T>(pColumnIdx, rowIdx);
967 T
GetCell(
const size_t pColumnIdx,
const std::string& pRowName, ConvFunc<T> pToVal)
const
969 const ssize_t rowIdx =
GetRowIdx(pRowName);
972 throw std::out_of_range(
"row not found: " + pRowName);
975 return GetCell<T>(pColumnIdx, rowIdx, pToVal);
985 void SetCell(
const size_t pColumnIdx,
const size_t pRowIdx,
const T& pCell)
987 const size_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
988 const size_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
990 while ((rowIdx + 1) > GetDataRowCount())
992 std::vector<std::string> row;
993 row.resize(GetDataColumnCount());
994 mData.push_back(row);
997 if ((columnIdx + 1) > GetDataColumnCount())
999 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
1001 itRow->resize(columnIdx + 1);
1007 converter.
ToStr(pCell, str);
1008 mData.at(rowIdx).at(columnIdx) = str;
1017 template<
typename T>
1018 void SetCell(
const std::string& pColumnName,
const std::string& pRowName,
const T& pCell)
1023 throw std::out_of_range(
"column not found: " + pColumnName);
1026 const ssize_t rowIdx =
GetRowIdx(pRowName);
1029 throw std::out_of_range(
"row not found: " + pRowName);
1032 SetCell<T>(columnIdx, rowIdx, pCell);
1042 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
1045 throw std::out_of_range(
"column name row index < 0: " + std::to_string(mLabelParams.
mColumnNameIdx));
1058 const ssize_t columnIdx = pColumnIdx + (mLabelParams.
mRowNameIdx + 1);
1059 mColumnNames[pColumnName] = columnIdx;
1062 throw std::out_of_range(
"column name row index < 0: " + std::to_string(mLabelParams.
mColumnNameIdx));
1067 if (rowIdx >= (
int) mData.size())
1069 mData.resize(rowIdx + 1);
1071 auto& row = mData[rowIdx];
1072 if (columnIdx >= (
int) row.size())
1074 row.resize(columnIdx + 1);
1077 mData.at(mLabelParams.
mColumnNameIdx).at(columnIdx) = pColumnName;
1088 return std::vector<std::string>(mData.at(mLabelParams.
mColumnNameIdx).begin() +
1093 return std::vector<std::string>();
1103 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
1106 throw std::out_of_range(
"row name column index < 0: " + std::to_string(mLabelParams.
mRowNameIdx));
1109 return mData.at(rowIdx).at(mLabelParams.
mRowNameIdx);
1119 const ssize_t rowIdx = pRowIdx + (mLabelParams.
mColumnNameIdx + 1);
1120 mRowNames[pRowName] = rowIdx;
1123 throw std::out_of_range(
"row name column index < 0: " + std::to_string(mLabelParams.
mRowNameIdx));
1127 if (rowIdx >= (
int) mData.size())
1129 mData.resize(rowIdx + 1);
1131 auto& row = mData[rowIdx];
1137 mData.at(rowIdx).at(mLabelParams.
mRowNameIdx) = pRowName;
1146 std::vector<std::string> rownames;
1149 for (
auto itRow = mData.begin(); itRow != mData.end(); ++itRow)
1151 if (std::distance(mData.begin(), itRow) > mLabelParams.
mColumnNameIdx)
1153 rownames.push_back(itRow->at(mLabelParams.
mRowNameIdx));
1163 std::ifstream stream;
1164 stream.exceptions(std::ifstream::failbit | std::ifstream::badbit);
1165 stream.open(mPath, std::ios::binary);
1169 void ReadCsv(std::istream& pStream)
1171 pStream.seekg(0, std::ios::end);
1172 std::streamsize length = pStream.tellg();
1173 pStream.seekg(0, std::ios::beg);
1176 std::vector<char> bom2b(2,
'\0');
1179 pStream.read(bom2b.data(), 2);
1180 pStream.seekg(0, std::ios::beg);
1183 static const std::vector<char> bomU16le = {
'\xff',
'\xfe' };
1184 static const std::vector<char> bomU16be = {
'\xfe',
'\xff' };
1185 if ((bom2b == bomU16le) || (bom2b == bomU16be))
1188 mIsLE = (bom2b == bomU16le);
1190 std::wifstream wstream;
1191 wstream.exceptions(std::wifstream::failbit | std::wifstream::badbit);
1192 wstream.open(mPath, std::ios::binary);
1195 wstream.imbue(std::locale(wstream.getloc(),
1196 new std::codecvt_utf16<
wchar_t, 0x10ffff,
1197 static_cast<std::codecvt_mode
>(std::consume_header |
1198 std::little_endian)>));
1202 wstream.imbue(std::locale(wstream.getloc(),
1203 new std::codecvt_utf16<
wchar_t, 0x10ffff,
1204 std::consume_header>));
1206 std::wstringstream wss;
1207 wss << wstream.rdbuf();
1208 std::string utf8 = ToString(wss.str());
1209 std::stringstream ss(utf8);
1210 ParseCsv(ss, utf8.size());
1218 std::vector<char> bom3b(3,
'\0');
1219 pStream.read(bom3b.data(), 3);
1220 static const std::vector<char> bomU8 = {
'\xef',
'\xbb',
'\xbf' };
1224 pStream.seekg(0, std::ios::beg);
1233 ParseCsv(pStream, length);
1237 void ParseCsv(std::istream& pStream, std::streamsize p_FileLength)
1239 const std::streamsize bufLength = 64 * 1024;
1240 std::vector<char> buffer(bufLength);
1241 std::vector<std::string> row;
1243 bool quoted =
false;
1247 while (p_FileLength > 0)
1249 std::streamsize readLength = std::min(p_FileLength, bufLength);
1250 pStream.read(buffer.data(), readLength);
1251 for (
int i = 0; i < readLength; ++i)
1253 if (buffer[i] ==
'"')
1255 if (cell.empty() || cell[0] ==
'"')
1261 else if (buffer[i] == mSeparatorParams.
mSeparator)
1265 row.push_back(Unquote(Trim(cell)));
1273 else if (buffer[i] ==
'\r')
1284 else if (buffer[i] ==
'\n')
1293 row.push_back(Unquote(Trim(cell)));
1295 mData.push_back(row);
1305 p_FileLength -= readLength;
1309 if (!cell.empty() || !row.empty())
1311 row.push_back(Unquote(Trim(cell)));
1313 mData.push_back(row);
1318 mSeparatorParams.
mHasCR = (cr > (lf / 2));
1322 (
static_cast<ssize_t
>(mData.size()) > mLabelParams.
mColumnNameIdx))
1327 mColumnNames[columnName] = i++;
1333 (
static_cast<ssize_t
>(mData.size()) >
1337 for (
auto& dataRow : mData)
1339 if (
static_cast<ssize_t
>(dataRow.size()) > mLabelParams.
mRowNameIdx)
1341 mRowNames[dataRow[mLabelParams.
mRowNameIdx]] = i++;
1347 void WriteCsv()
const
1352 std::stringstream ss;
1354 std::string utf8 = ss.str();
1355 std::wstring wstr = ToWString(utf8);
1357 std::wofstream wstream;
1358 wstream.exceptions(std::wofstream::failbit | std::wofstream::badbit);
1359 wstream.open(mPath, std::ios::binary | std::ios::trunc);
1363 wstream.imbue(std::locale(wstream.getloc(),
1364 new std::codecvt_utf16<
wchar_t, 0x10ffff,
1365 static_cast<std::codecvt_mode
>(std::little_endian)>));
1369 wstream.imbue(std::locale(wstream.getloc(),
1370 new std::codecvt_utf16<wchar_t, 0x10ffff>));
1373 wstream << (wchar_t) 0xfeff;
1379 std::ofstream stream;
1380 stream.exceptions(std::ofstream::failbit | std::ofstream::badbit);
1381 stream.open(mPath, std::ios::binary | std::ios::trunc);
1386 void WriteCsv(std::ostream& pStream)
const
1388 for (
auto itr = mData.begin(); itr != mData.end(); ++itr)
1390 for (
auto itc = itr->begin(); itc != itr->end(); ++itc)
1393 ((itc->find(mSeparatorParams.
mSeparator) != std::string::npos) ||
1394 (itc->find(
' ') != std::string::npos)))
1397 std::string str = *itc;
1398 ReplaceString(str,
"\"",
"\"\"");
1400 pStream <<
"\"" << str <<
"\"";
1407 if (std::distance(itc, itr->end()) > 1)
1412 pStream << (mSeparatorParams.
mHasCR ?
"\r\n" :
"\n");
1416 size_t GetDataRowCount()
const
1418 return mData.size();
1421 size_t GetDataColumnCount()
const
1423 return (mData.size() > 0) ? mData.at(0).size() : 0;
1426 std::string Trim(
const std::string& pStr)
1428 if (mSeparatorParams.
mTrim)
1430 std::string str = pStr;
1433 str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](
int ch) { return !isspace(ch); }));
1436 str.erase(std::find_if(str.rbegin(), str.rend(), [](
int ch) { return !isspace(ch); }).base(), str.end());
1446 std::string Unquote(
const std::string& pStr)
1448 if (mSeparatorParams.
mAutoQuote && (pStr.size() >= 2) && (pStr.front() ==
'"') && (pStr.back() ==
'"'))
1451 std::string str = pStr.substr(1, pStr.size() - 2);
1454 ReplaceString(str,
"\"\"",
"\"");
1465 #if defined(_MSC_VER)
1466 #pragma warning (disable: 4996)
1468 static std::string ToString(
const std::wstring& pWStr)
1470 size_t len = std::wcstombs(
nullptr, pWStr.c_str(), 0) + 1;
1471 char* cstr =
new char[len];
1472 std::wcstombs(cstr, pWStr.c_str(), len);
1473 std::string str(cstr);
1478 static std::wstring ToWString(
const std::string& pStr)
1480 size_t len = 1 + mbstowcs(
nullptr, pStr.c_str(), 0);
1481 wchar_t* wcstr =
new wchar_t[len];
1482 std::mbstowcs(wcstr, pStr.c_str(), len);
1483 std::wstring wstr(wcstr);
1487 #if defined(_MSC_VER)
1488 #pragma warning (default: 4996)
1492 static void ReplaceString(std::string& pStr,
const std::string& pSearch,
const std::string& pReplace)
1496 while ((pos = pStr.find(pSearch, pos)) != std::string::npos)
1498 pStr.replace(pos, pSearch.size(), pReplace);
1499 pos += pReplace.size();
1505 LabelParams mLabelParams;
1506 SeparatorParams mSeparatorParams;
1507 ConverterParams mConverterParams;
1508 std::vector<std::vector<std::string>> mData;
1509 std::map<std::string, size_t> mColumnNames;
1510 std::map<std::string, size_t> mRowNames;
1512 bool mIsUtf16 =
false;