ccuty  1.0
 All Classes Namespaces Files Functions Enumerations Macros Pages
ccstring.hpp
Go to the documentation of this file.
1 //
2 // ccstring: useful functions for C++ strings
3 // (C) Eric Lecolinet 2016/17 - https:www.telecom-paristech.fr/~elc
4 //
5 
11 #ifndef ccuty_ccstring
12 #define ccuty_ccstring
13 
14 #include <string>
15 #include <vector>
16 #include <sstream>
17 #include <cctype>
18 #include <cstdlib>
19 #include <cerrno>
20 #include <regex>
21 
23 namespace ccuty {
24 
47  inline constexpr unsigned long long strid(const char* str,
48  int last = 32767,
49  unsigned long long id = 0) {
50  return (!*str || last==0) ? id : ccuty::strid(str+1, last-1, (id<<8) + *str);
51  }
52 
56  inline unsigned long long strid(const std::string& str, int last = 32767) {
57  return ccuty::strid(str.c_str(), last, 0);
58  }
59 
62  struct strdelim {
63 
65  strdelim() {}
66 
68  strdelim(const std::string& delimiters) {pattern(delimiters);}
69 
71  strdelim(const char* delimiters) {pattern(delimiters);}
72 
74  void reset() {pos_ = 0; size_ = 0;}
75 
83  void pattern(const std::string& delimiters) {
84  pattern_ = delimiters;
85  if (delimiters.empty()) spaces_ = true;
86  }
87 
89  strdelim& spaces(bool val = true) {spaces_ = val; return *this;}
90 
92  strdelim& quotes(bool val = true) {quotes_ = val; return *this;}
93 
95  strdelim& trim(bool val = true) {trim_ = val; return *this;}
96 
98  void pos(size_t val) {pos_ = val;}
99 
101  size_t pos() const {return pos_;}
102 
104  char matched() const {return matched_;}
105 
106  // impl.
107  size_t pos_{0}, size_{0};
108  std::string pattern_;
109  char matched_{0};
110  bool spaces_{false}, quotes_{false}, trim_{true};
111  };
112 
143  inline size_t strsplit(std::vector<std::string>& tokens,
144  const std::string& str, ccuty::strdelim delimiters, int limit = 0);
145 
161  inline size_t strsplit(std::string& left, std::string& right,
162  const std::string& str, ccuty::strdelim delimiters);
163 
181  inline size_t regsplit(std::vector<std::string>& tokens,
182  const std::string& str, ccuty::strdelim delimiters, int limit = 0);
183 
195  bool strtoken(std::string& token, const std::string& str, ccuty::strdelim& delimiters);
196 
202  inline void strtrim(std::string& str, ccuty::strdelim toremove);
203 
205  inline void strtrim(std::string& str) {
207  }
208 
210  inline void unquote(std::string& str) {
211  strtrim(str, ccuty::strdelim("").quotes());
212  }
213 
215  inline std::string trimmed(const std::string& str) {
216  std::string result(str);
217  ccuty::strtrim(result, "");
218  return result;
219  }
220 
222  inline std::string unquoted(const std::string& str) {
223  std::string result(str);
224  ccuty::unquote(result);
225  return result;
226  }
227 
229  inline int icompare(const std::string& a, const std::string& b) {
230  size_t len = a.length() < b.length() ? a.length() : b.length();
231  for (size_t k=0; k<len; ++k) {
232  if (::tolower(a[k]) < ::tolower(b[k])) return -1;
233  else if (::tolower(a[k]) > ::tolower(b[k])) return +1;
234  }
235  return a.length()==b.length() ? 0 : a.length()<b.length() ? -1 : +1;
236  }
237 
239  inline bool iequal(const std::string& str1, const std::string& str2) {
240  return str1.length()!=str2.length() ? false : icompare(str1, str2)==0;
241  }
242 
244  inline void lowerize(std::string& str) {
245  for (auto& c : str) c = char(::tolower(c));
246  }
247 
249  inline void upperize(std::string& str) {
250  for (auto& c : str) c = char(::toupper(c));
251  }
252 
254  inline std::string tolower(const std::string& str) {
255  std::string s(str); ccuty::lowerize(s); return s;
256  }
257 
259  inline std::string toupper(const std::string& str) {
260  std::string s(str); ccuty::upperize(s); return s;
261  }
262 
264  inline bool from_string(bool& var, const std::string& str) {
265  if (str == "true") {var = true; return true;}
266  else if (str == "false") {var = false; return true;}
267  else {var = false; return false;}
268  }
269 
271  inline bool from_string(long& var, const std::string& str) {
272  char* p{nullptr};
273  errno = 0;
274  var = ::strtol(str.c_str(), &p, 0);
275  if (p == nullptr || errno != 0) {var = 0; return false;} else return true;
276  }
277 
279  inline bool from_string(unsigned long& var, const std::string& str) {
280  char* p{nullptr};
281  errno = 0;
282  var = ::strtoul(str.c_str(), &p, 0);
283  if (p == nullptr || errno != 0) {var = 0; return false;} else return true;
284  }
285 
287  inline bool from_string(float& var, const std::string& str) {
288  char* p{nullptr};
289  errno = 0;
290  var = ::strtof(str.c_str(), &p);
291  if (p == nullptr || errno != 0) {var = 0; return false;} else return true;
292  }
293 
295  inline bool from_string(double& var, const std::string& str) {
296  char* p{nullptr};
297  errno = 0;
298  var = ::strtod(str.c_str(), &p);
299  if (p == nullptr || errno != 0) {var = 0; return false;} else return true;
300  }
301 
303  inline bool from_string(long double& var, const std::string& str) {
304  char* p{nullptr};
305  errno = 0;
306  var = ::strtold(str.c_str(), &p);
307  if (p == nullptr || errno != 0) {var = 0; return false;} else return true;
308  }
309 
311  inline int system(const std::string& command) {
312  return ::system(command.c_str());
313  }
314 
315  // - - - implementation - - - - - - - - - - - - - - - - - - - - - - - - - - -
316 
317  inline void strtrim(std::string& str, ccuty::strdelim d) {
318  if (str.empty()) return;
319  if (d.size_ == 0) d.size_ = str.size();
320  if (d.pos_ >= d.size_) return;
321 
322  const char *p = str.c_str() + d.pos_;
323  const char *end = str.c_str() + d.size_;
324 
325  while (p < end // trim left
326  && ((d.spaces_ && ::isspace(*p))
327  || d.pattern_.find(*p) != std::string::npos)
328  ) ++p;
329  if (p >= end) {str.clear(); return;}
330 
331  --end;
332  while (end >= p // trim right
333  && ((d.spaces_ && ::isspace(*end))
334  || d.pattern_.find(*end) != std::string::npos)
335  ) --end;
336  if (end < p) {str.clear(); return;}
337 
338  if (d.quotes_) {
339  if (*p == '"' || *p == '\'') ++p;
340  if (*end == '"' || *end == '\'') --end;
341  if (end < p) {str.clear(); return;}
342  }
343  str.assign(p, size_t(end-p+1));
344  }
345 
346  inline bool strtoken(std::string& token, const std::string& str, ccuty::strdelim& d) {
347  token.clear();
348  d.matched_ = 0;
349  if (d.size_ == 0) d.size_ = str.size();
350  if (d.pos_ >= d.size_) {d.matched_ = 0; return false;}
351 
352  const char* p = str.c_str() + d.pos_;
353  char delim = 0;
354 
355  if (d.trim_) {
356  while (*p != 0 && ::isspace(*p)) ++p; // skip spaces
357  if (*p == 0) {d.pos_ = d.size_; d.matched_ = 0; return false;}
358  }
359  const char* begtok = p;
360 
361  if (d.quotes_ && (*p == '"' || *p == '\'')) { // quoted token
362  char quote = *p++;
363  while (*p != 0 && *p != quote) ++p;
364  if (*p == 0) { // ending quote not found, taking rest
365  token.assign(begtok+1, size_t(p-begtok));
366  d.pos_ = d.size_; d.matched_ = 0; return true;
367  }
368  else {
369  token.assign(begtok+1, size_t(p-begtok-1));
370  ++p; // after quote
371  delim = ::isspace(*p) ? ' ' : quote;
372  goto after_token;
373  }
374  }
375  else { // unquoted string
376  while (*p != 0 // find delim
377  && (!d.spaces_ || !::isspace(*p))
378  && (!d.quotes_ || (*p != '"' && *p != '\''))
379  && d.pattern_.find(*p) == std::string::npos) ++p;
380 
381  if (d.spaces_ && ::isspace(*p)) {
382  token.assign(begtok, size_t(p-begtok));
383  delim = ' ';
384  goto after_token;
385  }
386  else if (d.quotes_ && (*p == '"' || *p == '\'')) {
387  token.assign(begtok, size_t(p-begtok));
388  delim = *p;
389  goto after_token;
390  }
391  else {
392  const char* endtok = p-1;
393  if (d.trim_) {
394  while (endtok > begtok && ::isspace(*endtok)) --endtok;
395  }
396  token.assign(begtok, size_t(endtok-begtok+1));
397  if (*p == 0) {
398  d.pos_ = d.size_; d.matched_ = 0; return true;
399  }
400  else {
401  d.pos_ = size_t(p - str.c_str() + 1);
402  d.matched_ = *p;
403  return true;
404  }
405  }
406  }
407  return false;
408 
409  after_token:
410  if (d.trim_) {
411  while (*p != 0 && ::isspace(*p)) ++p;
412  }
413  if (*p == 0) {
414  d.pos_ = d.size_; d.matched_ = 0; return true;
415  }
416  // nb: returns delim rather then space when both conds apply
417  else if (d.pattern_.find(*p) != std::string::npos) {
418  d.pos_ = size_t(p - str.c_str() + 1);
419  d.matched_ = *p;
420  return true;
421  }
422  else { // delim is quote or space
423  d.pos_ = size_t(p - str.c_str() + (d.trim_ ? 0 : 1));
424  d.matched_ = delim;
425  return true;
426  }
427  }
428 
429  inline size_t strsplit(std::string& left, std::string& right, const std::string& str,
430  ccuty::strdelim delim) {
431  std::string s(str); // str may be the same string as left or right
432  left.clear(); right.clear();
433  size_t n{0};
434  if (ccuty::strtoken(left, s, delim)) n++;
435  delim.pattern_.clear();
436  delim.spaces(false);
437  if (n==0) {if (ccuty::strtoken(left, s, delim)) n++;}
438  else {if (ccuty::strtoken(right, s, delim)) n++;}
439  return n;
440  }
441 
442  inline size_t strsplit(std::vector<std::string>& tokens, const std::string& str,
443  ccuty::strdelim delim, int limit) {
444  tokens.clear();
445  std::string tok;
446  if (limit <= 0) limit = 32767;
447  while (int(tokens.size()) < limit-1 && ccuty::strtoken(tok, str, delim)) tokens.push_back(tok);
448  delim.pattern_.clear();
449  delim.spaces(false);
450  if (ccuty::strtoken(tok, str, delim) && !tok.empty()) tokens.push_back(tok);
451  return tokens.size();
452  }
453 
454  inline size_t regsplit(std::vector<std::string>& tokens, const std::string& str,
455  ccuty::strdelim delim, int limit) {
456  tokens.clear();
457  if (str.empty()) return 0;
458  std::regex expr(delim.spaces_ ? "[:space:]"+delim.pattern_ : delim.pattern_);
459  const char *p = str.c_str(), *prefix = p;
460 
461  auto trimRight = [&tokens](const char* beg, const char* end, bool atEnd) {
462  while (end >= beg && ::isspace(*end)) --end;
463  if (end < beg) {if (!atEnd) tokens.push_back("");}
464  else tokens.push_back(std::string(beg, end-beg+1));
465  };
466 
467  while (limit <= 0 || int(tokens.size()) < limit-1) {
468  if (delim.trim_) { // trim left
469  while (*p != 0 && ::isspace(*p)) ++p;
470  if (*p == 0) {prefix = p; break;}
471  }
472  prefix = p;
473  std::cmatch m;
474  if (!std::regex_search(p, m, expr)) break;
475  else {
476  if (!delim.trim_) tokens.push_back(std::string(prefix, p-prefix+m.position()));
477  else (trimRight)(prefix, p+m.position()-1, false);
478  p += m.position() + m.length();
479  prefix = p;
480  }
481  }
482 
483  if (*prefix) {
484  if (!delim.trim_) tokens.push_back(std::string(prefix));
485  else {
486  while (*prefix != 0 && ::isspace(*prefix)) ++prefix;
487  if (*prefix != 0) (trimRight)(prefix, prefix+::strlen(prefix)-1, true);
488  }
489  }
490  return tokens.size();
491  }
492 }
493 
494 #endif