CLI11
C++11 Command Line Interface Parser
StringTools.hpp
1 #pragma once
2 
3 // Distributed under the 3-Clause BSD License. See accompanying
4 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5 
6 #include <algorithm>
7 #include <iomanip>
8 #include <locale>
9 #include <sstream>
10 #include <stdexcept>
11 #include <string>
12 #include <type_traits>
13 #include <vector>
14 
15 namespace CLI {
16 
19 namespace enums {
20 
22 template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
23 std::ostream &operator<<(std::ostream &in, const T &item) {
24  // make sure this is out of the detail namespace otherwise it won't be found when needed
25  return in << static_cast<typename std::underlying_type<T>::type>(item);
26 }
27 
29 template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
30 std::istream &operator>>(std::istream &in, T &item) {
31  typename std::underlying_type<T>::type i;
32  in >> i;
33  item = static_cast<T>(i);
34  return in;
35 }
36 } // namespace enums
37 
39 using namespace enums;
40 
41 namespace detail {
42 
43 // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
45 inline std::vector<std::string> split(const std::string &s, char delim) {
46  std::vector<std::string> elems;
47  // Check to see if empty string, give consistent result
48  if(s.empty())
49  elems.emplace_back();
50  else {
51  std::stringstream ss;
52  ss.str(s);
53  std::string item;
54  while(std::getline(ss, item, delim)) {
55  elems.push_back(item);
56  }
57  }
58  return elems;
59 }
61 template <typename T> inline std::string as_string(const T &v) {
62  std::ostringstream s;
63  s << v;
64  return s.str();
65 }
66 // if the data type is already a string just forward it
67 template <typename T, typename = typename std::enable_if<std::is_constructible<std::string, T>::value>::type>
68 inline auto as_string(T &&v) -> decltype(std::forward<T>(v)) {
69  return std::forward<T>(v);
70 }
71 
73 template <typename T> std::string join(const T &v, std::string delim = ",") {
74  std::ostringstream s;
75  auto beg = std::begin(v);
76  auto end = std::end(v);
77  if(beg != end)
78  s << *beg++;
79  while(beg != end) {
80  s << delim << *beg++;
81  }
82  return s.str();
83 }
84 
86 template <typename T,
87  typename Callable,
88  typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::value>::type>
89 std::string join(const T &v, Callable func, std::string delim = ",") {
90  std::ostringstream s;
91  auto beg = std::begin(v);
92  auto end = std::end(v);
93  if(beg != end)
94  s << func(*beg++);
95  while(beg != end) {
96  s << delim << func(*beg++);
97  }
98  return s.str();
99 }
100 
102 template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
103  std::ostringstream s;
104  for(size_t start = 0; start < v.size(); start++) {
105  if(start > 0)
106  s << delim;
107  s << v[v.size() - start - 1];
108  }
109  return s.str();
110 }
111 
112 // Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
113 
115 inline std::string &ltrim(std::string &str) {
116  auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
117  str.erase(str.begin(), it);
118  return str;
119 }
120 
122 inline std::string &ltrim(std::string &str, const std::string &filter) {
123  auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
124  str.erase(str.begin(), it);
125  return str;
126 }
127 
129 inline std::string &rtrim(std::string &str) {
130  auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
131  str.erase(it.base(), str.end());
132  return str;
133 }
134 
136 inline std::string &rtrim(std::string &str, const std::string &filter) {
137  auto it =
138  std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
139  str.erase(it.base(), str.end());
140  return str;
141 }
142 
144 inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
145 
147 inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
148 
150 inline std::string trim_copy(const std::string &str) {
151  std::string s = str;
152  return trim(s);
153 }
154 
156 inline std::string trim_copy(const std::string &str, const std::string &filter) {
157  std::string s = str;
158  return trim(s, filter);
159 }
161 inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, size_t wid) {
162  name = " " + name;
163  out << std::setw(static_cast<int>(wid)) << std::left << name;
164  if(!description.empty()) {
165  if(name.length() >= wid)
166  out << "\n" << std::setw(static_cast<int>(wid)) << "";
167  for(const char c : description) {
168  out.put(c);
169  if(c == '\n') {
170  out << std::setw(static_cast<int>(wid)) << "";
171  }
172  }
173  }
174  out << "\n";
175  return out;
176 }
177 
179 template <typename T> bool valid_first_char(T c) {
180  return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@';
181 }
182 
184 template <typename T> bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; }
185 
187 inline bool valid_name_string(const std::string &str) {
188  if(str.empty() || !valid_first_char(str[0]))
189  return false;
190  for(auto c : str.substr(1))
191  if(!valid_later_char(c))
192  return false;
193  return true;
194 }
195 
197 inline bool isalpha(const std::string &str) {
198  return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
199 }
200 
202 inline std::string to_lower(std::string str) {
203  std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
204  return std::tolower(x, std::locale());
205  });
206  return str;
207 }
208 
210 inline std::string remove_underscore(std::string str) {
211  str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
212  return str;
213 }
214 
216 inline std::string find_and_replace(std::string str, std::string from, std::string to) {
217 
218  size_t start_pos = 0;
219 
220  while((start_pos = str.find(from, start_pos)) != std::string::npos) {
221  str.replace(start_pos, from.length(), to);
222  start_pos += to.length();
223  }
224 
225  return str;
226 }
227 
229 inline bool has_default_flag_values(const std::string &flags) {
230  return (flags.find_first_of("{!") != std::string::npos);
231 }
232 
233 inline void remove_default_flag_values(std::string &flags) {
234  auto loc = flags.find_first_of('{');
235  while(loc != std::string::npos) {
236  auto finish = flags.find_first_of("},", loc + 1);
237  if((finish != std::string::npos) && (flags[finish] == '}')) {
238  flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
239  flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
240  }
241  loc = flags.find_first_of('{', loc + 1);
242  }
243  flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
244 }
245 
247 inline std::ptrdiff_t find_member(std::string name,
248  const std::vector<std::string> names,
249  bool ignore_case = false,
250  bool ignore_underscore = false) {
251  auto it = std::end(names);
252  if(ignore_case) {
253  if(ignore_underscore) {
254  name = detail::to_lower(detail::remove_underscore(name));
255  it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
256  return detail::to_lower(detail::remove_underscore(local_name)) == name;
257  });
258  } else {
259  name = detail::to_lower(name);
260  it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
261  return detail::to_lower(local_name) == name;
262  });
263  }
264 
265  } else if(ignore_underscore) {
266  name = detail::remove_underscore(name);
267  it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
268  return detail::remove_underscore(local_name) == name;
269  });
270  } else
271  it = std::find(std::begin(names), std::end(names), name);
272 
273  return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
274 }
275 
278 template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) {
279  size_t start_pos = 0;
280  while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
281  start_pos = modify(str, start_pos);
282  }
283  return str;
284 }
285 
288 inline std::vector<std::string> split_up(std::string str) {
289 
290  const std::string delims("\'\"`");
291  auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); };
292  trim(str);
293 
294  std::vector<std::string> output;
295  bool embeddedQuote = false;
296  char keyChar = ' ';
297  while(!str.empty()) {
298  if(delims.find_first_of(str[0]) != std::string::npos) {
299  keyChar = str[0];
300  auto end = str.find_first_of(keyChar, 1);
301  while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes
302  end = str.find_first_of(keyChar, end + 1);
303  embeddedQuote = true;
304  }
305  if(end != std::string::npos) {
306  output.push_back(str.substr(1, end - 1));
307  str = str.substr(end + 1);
308  } else {
309  output.push_back(str.substr(1));
310  str = "";
311  }
312  } else {
313  auto it = std::find_if(std::begin(str), std::end(str), find_ws);
314  if(it != std::end(str)) {
315  std::string value = std::string(str.begin(), it);
316  output.push_back(value);
317  str = std::string(it, str.end());
318  } else {
319  output.push_back(str);
320  str = "";
321  }
322  }
323  // transform any embedded quotes into the regular character
324  if(embeddedQuote) {
325  output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar));
326  embeddedQuote = false;
327  }
328  trim(str);
329  }
330  return output;
331 }
332 
337 inline std::string fix_newlines(std::string leader, std::string input) {
338  std::string::size_type n = 0;
339  while(n != std::string::npos && n < input.size()) {
340  n = input.find('\n', n);
341  if(n != std::string::npos) {
342  input = input.substr(0, n + 1) + leader + input.substr(n + 1);
343  n += leader.size();
344  }
345  }
346  return input;
347 }
348 
353 inline size_t escape_detect(std::string &str, size_t offset) {
354  auto next = str[offset + 1];
355  if((next == '\"') || (next == '\'') || (next == '`')) {
356  auto astart = str.find_last_of("-/ \"\'`", offset - 1);
357  if(astart != std::string::npos) {
358  if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
359  str[offset] = ' '; // interpret this as a space so the split_up works properly
360  }
361  }
362  return offset + 1;
363 }
364 
366 inline std::string &add_quotes_if_needed(std::string &str) {
367  if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) {
368  char quote = str.find('"') < str.find('\'') ? '\'' : '"';
369  if(str.find(' ') != std::string::npos) {
370  str.insert(0, 1, quote);
371  str.append(1, quote);
372  }
373  }
374  return str;
375 }
376 
377 } // namespace detail
378 
379 } // namespace CLI
Definition: App.hpp:29
std::istream & operator>>(std::istream &in, T &item)
input streaming for enumerations
Definition: StringTools.hpp:30
std::ostream & operator<<(std::ostream &in, const T &item)
output streaming for enumerations
Definition: StringTools.hpp:23