OSVR-Core
CSVTools.h
Go to the documentation of this file.
1 
11 // Copyright 2016 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 #ifndef INCLUDED_CSVTools_h_GUID_82FA298C_196A_46AA_B2D6_059F2A035687
26 #define INCLUDED_CSVTools_h_GUID_82FA298C_196A_46AA_B2D6_059F2A035687
27 
28 // Internal Includes
29 // - none
30 
31 // Library/third-party includes
32 // - none
33 
34 // Standard includes
35 #include <cassert>
36 #include <cstddef>
37 #include <iosfwd>
38 #include <iterator>
39 #include <sstream>
40 #include <stdexcept>
41 #include <string>
42 #include <vector>
43 
44 namespace csvtools {
45 
46 static const char COMMA_CHAR = ',';
47 
48 static const char DOUBLEQUOTE_CHAR = '"';
49 
50 inline std::string getCleanLine(std::istream &is) {
51  std::string ret;
52  std::getline(is, ret);
53  while (!ret.empty() && (ret.back() == '\n' || ret.back() == '\r')) {
54  ret.pop_back();
55  }
56  return ret;
57 }
58 
59 namespace string_fields {
60 
61  inline std::size_t getBeginningOfField(std::string const &line,
62  std::size_t field) {
63  if (0 == field) {
64  return 0;
65  }
66  std::size_t pos = 0;
67  for (std::size_t i = 0; i < field && pos != std::string::npos; ++i) {
68  pos = line.find(COMMA_CHAR, pos + 1);
69  }
70  if (pos != std::string::npos) {
71  if (pos + 1 < line.size()) {
72  // must advance past the comma.
73  pos++;
74  } else {
75  // if we can't advance past the comma, it's as though we
76  // couldn't
77  // find the field.
78  pos = std::string::npos;
79  }
80  }
81  return pos;
82  }
83 }
84 
85 #define CSV_TESTING
86 
87 #ifdef CSV_TESTING
88 #define CSV_VERIFY(X, MSG) \
89  do { \
90  if (!(X)) { \
91  throw std::logic_error(MSG); \
92  } \
93  } while (0)
94 #else
95 #define CSV_VERIFY(X, MSG)
96 #endif
97 
98 class StringField {
99  public:
100  using iterator = std::string::const_iterator;
101  StringField(std::string const &line, const std::size_t b,
102  const std::size_t e)
103  : line_(line), b_(b), e_(e), ve_(virtualEndPos(line_, e_)) {
104  checkInvariants();
105  }
106 
107  StringField(StringField const &other, const std::size_t b,
108  const std::size_t e)
109  : line_(other.line_), b_(b), e_(e_), ve_(virtualEndPos(line_, e_)) {
110  checkInvariants();
111  }
112 
113  StringField(StringField const &other) = default;
114 
116  std::size_t beginPos() const { return b_; }
117 
120  std::size_t virtualEndPos() const { return ve_; }
121 
124  std::size_t endPos() const { return e_; }
125 
127  std::size_t realLength() const { return virtualEndPos() - b_; }
128 
131  std::size_t lengthForSubstr() const {
132  return (e_ == std::string::npos) ? std::string::npos : e_ - b_;
133  }
134 
136  bool empty() const { return beginPos() == virtualEndPos(); }
137 
139  std::string const &getLine() const { return line_; }
140 
142  std::string substr() const {
143  return line_.substr(beginPos(), lengthForSubstr());
144  }
145 
147  iterator begin() const { return getIteratorAt(beginPos()); }
148 
150  iterator end() const {
151  if (e_ == std::string::npos) {
152  return line_.cend();
153  } else {
154  return getIteratorAt(endPos());
155  }
156  }
157 
158  char front() const {
159  if (empty()) {
160  throw std::out_of_range(
161  "can't get front element of an empty string field");
162  }
163  return line_[beginPos()];
164  }
165 
166  char back() const {
167  if (empty()) {
168  throw std::out_of_range(
169  "can't get back element of an empty string field");
170  }
171  return line_[virtualEndPos() - 1];
172  }
173 
177  if (realLength() < 2) {
179  return StringField(*this);
180  }
181  if (front() != DOUBLEQUOTE_CHAR || back() != DOUBLEQUOTE_CHAR) {
183  return StringField(*this);
184  }
185  auto newB = beginPos() + 1;
186  auto newE = virtualEndPos() - 1;
187  return StringField(*this, newB, newE);
188  }
189 
190  void loadIntoStringStream(std::istringstream &iss) const {
193  iss.str(substr());
194  iss.clear();
195  }
196 
197  private:
198  iterator getIteratorAt(const std::size_t pos) const {
199  auto it = line_.cbegin();
200  std::advance(it, pos);
201  return it;
202  }
203  static std::size_t virtualEndPos(std::string const &line,
204  const std::size_t e) {
205  return (e == std::string::npos) ? line.size() : e;
206  }
207  void checkInvariants() const {
208  assert((e_ == std::string::npos || e_ < line_.size()) &&
209  "past the end position must be npos or a valid position");
210  CSV_VERIFY((e_ == std::string::npos || e_ < line_.size()),
211  "past the end position must be npos or a valid position");
212  CSV_VERIFY((b_ < line_.size()),
213  "Begin position must be a valid position");
214  CSV_VERIFY((e_ == std::string::npos || b_ < e_),
215  "Begin position must be strictly before past the end.");
216  CSV_VERIFY((b_ < ve_), "Begin position must be strictly before the "
217  "virtual past the end.");
218 #if 0
219  CSV_VERIFY((e_ == std::string::npos || line_[e_] == COMMA_CHAR),
221  "past the end position must be npos or hold a delimiter.");
222 #endif
223  }
224  std::string const &line_;
225  const std::size_t b_;
226  const std::size_t e_;
227  const std::size_t ve_;
228 };
229 
231  public:
232  template <typename T>
233  inline bool getField(StringField const &field, T &output) {
234  field.loadIntoStringStream(iss_);
235  return static_cast<bool>(iss_ >> output);
236  }
237 
238  template <typename T>
239  inline std::pair<bool, T> getFieldAs(StringField const &field) {
240  T ret = T{};
241  field.loadIntoStringStream(iss_);
242  auto success = static_cast<bool>(iss_ >> ret);
243  return std::make_pair(success, ret);
244  }
245 
246  private:
247  std::istringstream iss_;
248 };
249 
253 template <typename F>
254 inline void iterateFields(F &&fieldFunc, std::string const &line,
255  std::size_t numFields = std::string::npos,
256  std::size_t first = 0) {
258  std::size_t b = string_fields::getBeginningOfField(line, first);
260  auto e = b;
261  std::size_t len = 0;
262  bool keepGoing = true;
263  const auto n = line.size();
266  for (std::size_t i = 0; i < numFields && keepGoing && b < n; ++i) {
267  e = line.find(COMMA_CHAR, b);
268  keepGoing = fieldFunc(line, b, e);
269  if (e == std::string::npos) {
270  keepGoing = false;
271  }
272  if (keepGoing) {
273  b = e + 1;
274  }
275  }
276 }
277 
278 inline std::vector<std::string>
279 getFields(std::string const &line, std::size_t numFields = std::string::npos,
280  std::size_t first = 0) {
281  std::vector<std::string> ret;
282  auto fieldFunc = [&](std::string const &line, std::size_t beginPos,
283  std::size_t endPos) {
284  bool lambdaRet = true;
285  std::size_t len;
286  if (endPos == std::string::npos) {
287  // indicate to substring we want the rest of the line.
288  len = std::string::npos;
289  // quit after this field
290  lambdaRet = false;
291  } else {
292  len = endPos - beginPos;
293  }
294  ret.emplace_back(line.substr(beginPos, len));
295  return lambdaRet;
296  };
297  iterateFields(fieldFunc, line, numFields, first);
298 
299  return ret;
300 }
301 
302 inline void stripQuotes(std::string &field) {
303  if (field.size() > 1 && field.front() == DOUBLEQUOTE_CHAR &&
304  field.back() == DOUBLEQUOTE_CHAR) {
306  field.pop_back();
308  field.erase(0, 1);
309  }
310 }
311 
312 inline void stripQuotes(std::vector<std::string> &fields) {
313  for (auto &field : fields) {
314  stripQuotes(field);
315  }
316 }
317 } // csvtools
318 
319 #endif // INCLUDED_CSVTools_h_GUID_82FA298C_196A_46AA_B2D6_059F2A035687
iterator begin() const
Get a begin iterator.
Definition: CSVTools.h:147
iterator end() const
Get an end iterator.
Definition: CSVTools.h:150
std::string substr() const
Make a copy of the range as a substring.
Definition: CSVTools.h:142
std::size_t beginPos() const
return the beginning position, which is a valid position in the string.
Definition: CSVTools.h:116
Definition: CSVTools.h:44
std::size_t endPos() const
return the one-past-the-end position, which may be std::string::npos if the range includes the rest o...
Definition: CSVTools.h:124
Definition: CSVTools.h:98
bool empty() const
Is the field/range empty?
Definition: CSVTools.h:136
void stripQuotes(std::string &field)
Definition: CSVTools.h:302
void loadIntoStringStream(std::istringstream &iss) const
Definition: CSVTools.h:190
StringField stripQuotes() const
Get a StringField object based on this one where outer quotation marks, if present, have been removed.
Definition: CSVTools.h:176
std::size_t virtualEndPos() const
return the virtual one-past-the-end position - may point one past the end of the array but isn&#39;t std:...
Definition: CSVTools.h:120
Definition: CSVTools.h:230
std::size_t realLength() const
gets the length of the field
Definition: CSVTools.h:127
std::string const & getLine() const
Access (by reference) the full line.
Definition: CSVTools.h:139
return true to continue to next field *void iterateFields(F &&fieldFunc, std::string const &line, std::size_t numFields=std::string::npos, std::size_t first=0)
Definition: CSVTools.h:254
std::size_t lengthForSubstr() const
like realLength, except returns std::string::npos when the end includes the rest of the string...
Definition: CSVTools.h:131