csv-parser 1.1.0
Csv::Parser (csv-parser)
Loading...
Searching...
No Matches
csv_util.h
Go to the documentation of this file.
1/**************************************************************************
2Copyright: (C) 2021 - 2025 Alexander Shaduri
3License: Zlib
4***************************************************************************/
5
6#ifndef CSV_UTIL_H
7#define CSV_UTIL_H
8
9#include <string_view>
10#include <string>
11#include <optional>
12#include <algorithm>
13#include <charconv>
14
15
22namespace Csv {
23
24
25namespace internal {
26
29 template<typename T>
30 struct always_false : std::false_type {};
31
32}
33
34
35
39inline std::string cleanString(std::string_view view);
40
41
45inline constexpr std::size_t getCleanStringSize(std::string_view uncollapsed_view);
46
47
54template<typename Number>
55std::optional<Number> readNumberLocale(std::string_view cell);
56
57
65template<typename Number>
66#if __cplusplus >= 202300L
67constexpr
68#endif
69std::optional<Number> readNumberNoLocale(std::string_view cell);
70
71
78template<typename Vector2D>
79static constexpr auto vector2DValue(const Vector2D& values, std::size_t row, std::size_t column);
80
81
82
83
84// ----- Implementation
85
86
87
88std::string cleanString(std::string_view view)
89{
90 std::string s;
91 s.reserve(view.size());
92 for (std::size_t pos = 0; pos < view.size(); ++pos) {
93 char c = view[pos];
94 s += c;
95 if (c == '\"' && (pos + 1) < view.size() && view[pos + 1] == '\"') {
96 ++pos;
97 }
98 }
99 return s;
100}
101
102
103
104constexpr std::size_t getCleanStringSize(std::string_view uncollapsed_view)
105{
106 // Count the number of quotes in the string. Since quotes are doubled,
107 // subtract half.
108 // std::count is not constexpr in C++17, so we have to do it manually.
109 std::size_t quote_count = 0;
110 for (char c : uncollapsed_view) {
111 if (c == '\"') {
112 ++quote_count;
113 }
114 }
115 return uncollapsed_view.size() - (quote_count / 2);
116}
117
118
119
120template<typename Number>
121std::optional<Number> readNumberLocale(std::string_view cell)
122{
123 // Trim right whitespace (left whitespace is ignored by stod()).
124 std::size_t size = cell.size();
125 if (auto end_pos = cell.find_last_not_of(" \t"); end_pos != std::string_view::npos) {
126 size = end_pos + 1;
127 }
128 std::string s(cell.data(), size);
129
130 // Convert to lowercase (needed for Matlab-produced CSV files)
131 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
132 return static_cast<char>(std::tolower(c));
133 });
134
135 std::optional<Number> numeric_value;
136 // Use locale-aware conversion functions to parse the number.
137 // While calling std::strtod() could be potentially faster, it also means we have to deal with some
138 // platform-specific errno and other peculiarities. std::stod() wraps that nicely.
139 try {
140 std::size_t num_processed = 0;
141 Number parsed_value;
142
143 // We have to use a 0-terminated string in std::sto*() functions.
144 if constexpr(std::is_same_v<Number, float>) {
145 parsed_value = std::stof(s, &num_processed);
146 } else if constexpr(std::is_same_v<Number, double>) {
147 parsed_value = std::stod(s, &num_processed);
148 } else if constexpr(std::is_same_v<Number, long double>) {
149 parsed_value = std::stold(s, &num_processed);
150
151 } else if constexpr(std::is_same_v<Number, int>) {
152 parsed_value = std::stoi(s, &num_processed);
153 } else if constexpr(std::is_same_v<Number, long int>) {
154 parsed_value = std::stol(s, &num_processed);
155 } else if constexpr(std::is_same_v<Number, long long int>) {
156 parsed_value = std::stoll(s, &num_processed);
157
158 } else if constexpr(std::is_same_v<Number, unsigned long int>) {
159 parsed_value = std::stoul(s, &num_processed);
160 } else if constexpr(std::is_same_v<Number, unsigned long long int>) {
161 parsed_value = std::stoull(s, &num_processed);
162
163 } else {
164 static_assert(internal::always_false<Number>::value, "Invalid Number type in Csv::readNumber()");
165 }
166
167 if (num_processed == s.size()) {
168 numeric_value = parsed_value;
169 }
170 } catch (...) {
171 // nothing
172 }
173 return numeric_value;
174}
175
176
177
178template<typename Number>
179#if __cplusplus >= 202300L
180constexpr
181#endif
182std::optional<Number> readNumberNoLocale(std::string_view cell)
183{
184 // Trim right and left whitespace
185 cell.remove_prefix(std::min(cell.find_first_not_of(" \t"), cell.size()));
186 cell.remove_suffix(std::min(cell.size() - cell.find_last_not_of(" \t") - 1, cell.size()));
187
188 // string can be used in constexpr functions since C++23
189 std::string s(cell);
190
191 // Convert to lowercase (needed for Matlab-produced CSV files)
192 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
193 return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; // tolower is not constexpr
194 });
195
196 Number parsed_value = 0;
197
198 // from_chars is constexpr since C++23, but only for integers
199 auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), parsed_value);
200 if (ec == std::errc() && ptr == (s.data() + s.size())) {
201 return parsed_value;
202 }
203 return std::nullopt;
204}
205
206
207
208template<typename Vector2D>
209constexpr auto vector2DValue(const Vector2D& values, std::size_t row, std::size_t column)
210{
211 return values.at(column).at(row);
212}
213
214
215
216} // end ns
217
218
219
220#endif
std::optional< Number > readNumberLocale(std::string_view cell)
Try to read a numeric value from string data.
Definition csv_util.h:121
std::optional< Number > readNumberNoLocale(std::string_view cell)
Try to read a numeric value from string data.
Definition csv_util.h:182
constexpr std::size_t getCleanStringSize(std::string_view uncollapsed_view)
Get size of a cleaned-up string given the uncollapsed string.
Definition csv_util.h:104
std::string cleanString(std::string_view view)
Unescape a string - collapse every occurrence of 2 consecutive double-quotes to one.
Definition csv_util.h:88