CLI11  1.9.0
StringTools.hpp
Go to the documentation of this file.
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 
28 } // namespace enums
29 
31 using enums::operator<<;
32 
33 namespace detail {
36 constexpr int expected_max_vector_size{1 << 29};
37 // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
39 inline std::vector<std::string> split(const std::string &s, char delim) {
40  std::vector<std::string> elems;
41  // Check to see if empty string, give consistent result
42  if(s.empty())
43  elems.emplace_back();
44  else {
45  std::stringstream ss;
46  ss.str(s);
47  std::string item;
48  while(std::getline(ss, item, delim)) {
49  elems.push_back(item);
50  }
51  }
52  return elems;
53 }
54 
56 template <typename T> std::string join(const T &v, std::string delim = ",") {
57  std::ostringstream s;
58  auto beg = std::begin(v);
59  auto end = std::end(v);
60  if(beg != end)
61  s << *beg++;
62  while(beg != end) {
63  s << delim << *beg++;
64  }
65  return s.str();
66 }
67 
69 template <typename T,
70  typename Callable,
71  typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::value>::type>
72 std::string join(const T &v, Callable func, std::string delim = ",") {
73  std::ostringstream s;
74  auto beg = std::begin(v);
75  auto end = std::end(v);
76  if(beg != end)
77  s << func(*beg++);
78  while(beg != end) {
79  s << delim << func(*beg++);
80  }
81  return s.str();
82 }
83 
85 template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
86  std::ostringstream s;
87  for(std::size_t start = 0; start < v.size(); start++) {
88  if(start > 0)
89  s << delim;
90  s << v[v.size() - start - 1];
91  }
92  return s.str();
93 }
94 
95 // Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
96 
98 inline std::string &ltrim(std::string &str) {
99  auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
100  str.erase(str.begin(), it);
101  return str;
102 }
103 
105 inline std::string &ltrim(std::string &str, const std::string &filter) {
106  auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
107  str.erase(str.begin(), it);
108  return str;
109 }
110 
112 inline std::string &rtrim(std::string &str) {
113  auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
114  str.erase(it.base(), str.end());
115  return str;
116 }
117 
119 inline std::string &rtrim(std::string &str, const std::string &filter) {
120  auto it =
121  std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
122  str.erase(it.base(), str.end());
123  return str;
124 }
125 
127 inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
128 
130 inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
131 
133 inline std::string trim_copy(const std::string &str) {
134  std::string s = str;
135  return trim(s);
136 }
137 
139 inline std::string &remove_quotes(std::string &str) {
140  if(str.length() > 1 && (str.front() == '"' || str.front() == '\'')) {
141  if(str.front() == str.back()) {
142  str.pop_back();
143  str.erase(str.begin(), str.begin() + 1);
144  }
145  }
146  return str;
147 }
148 
150 inline std::string trim_copy(const std::string &str, const std::string &filter) {
151  std::string s = str;
152  return trim(s, filter);
153 }
155 inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, std::size_t wid) {
156  name = " " + name;
157  out << std::setw(static_cast<int>(wid)) << std::left << name;
158  if(!description.empty()) {
159  if(name.length() >= wid)
160  out << "\n" << std::setw(static_cast<int>(wid)) << "";
161  for(const char c : description) {
162  out.put(c);
163  if(c == '\n') {
164  out << std::setw(static_cast<int>(wid)) << "";
165  }
166  }
167  }
168  out << "\n";
169  return out;
170 }
171 
173 template <typename T> bool valid_first_char(T c) {
174  return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@';
175 }
176 
178 template <typename T> bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; }
179 
181 inline bool valid_name_string(const std::string &str) {
182  if(str.empty() || !valid_first_char(str[0]))
183  return false;
184  for(auto c : str.substr(1))
185  if(!valid_later_char(c))
186  return false;
187  return true;
188 }
189 
191 inline bool isalpha(const std::string &str) {
192  return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
193 }
194 
196 inline std::string to_lower(std::string str) {
197  std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
198  return std::tolower(x, std::locale());
199  });
200  return str;
201 }
202 
204 inline std::string remove_underscore(std::string str) {
205  str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
206  return str;
207 }
208 
210 inline std::string find_and_replace(std::string str, std::string from, std::string to) {
211 
212  std::size_t start_pos = 0;
213 
214  while((start_pos = str.find(from, start_pos)) != std::string::npos) {
215  str.replace(start_pos, from.length(), to);
216  start_pos += to.length();
217  }
218 
219  return str;
220 }
221 
223 inline bool has_default_flag_values(const std::string &flags) {
224  return (flags.find_first_of("{!") != std::string::npos);
225 }
226 
227 inline void remove_default_flag_values(std::string &flags) {
228  auto loc = flags.find_first_of('{');
229  while(loc != std::string::npos) {
230  auto finish = flags.find_first_of("},", loc + 1);
231  if((finish != std::string::npos) && (flags[finish] == '}')) {
232  flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
233  flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
234  }
235  loc = flags.find_first_of('{', loc + 1);
236  }
237  flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
238 }
239 
241 inline std::ptrdiff_t find_member(std::string name,
242  const std::vector<std::string> names,
243  bool ignore_case = false,
244  bool ignore_underscore = false) {
245  auto it = std::end(names);
246  if(ignore_case) {
247  if(ignore_underscore) {
249  it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
250  return detail::to_lower(detail::remove_underscore(local_name)) == name;
251  });
252  } else {
253  name = detail::to_lower(name);
254  it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
255  return detail::to_lower(local_name) == name;
256  });
257  }
258 
259  } else if(ignore_underscore) {
260  name = detail::remove_underscore(name);
261  it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
262  return detail::remove_underscore(local_name) == name;
263  });
264  } else
265  it = std::find(std::begin(names), std::end(names), name);
266 
267  return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
268 }
269 
272 template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) {
273  std::size_t start_pos = 0;
274  while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
275  start_pos = modify(str, start_pos);
276  }
277  return str;
278 }
279 
282 inline std::vector<std::string> split_up(std::string str, char delimiter = '\0') {
283 
284  const std::string delims("\'\"`");
285  auto find_ws = [delimiter](char ch) {
286  return (delimiter == '\0') ? (std::isspace<char>(ch, std::locale()) != 0) : (ch == delimiter);
287  };
288  trim(str);
289 
290  std::vector<std::string> output;
291  bool embeddedQuote = false;
292  char keyChar = ' ';
293  while(!str.empty()) {
294  if(delims.find_first_of(str[0]) != std::string::npos) {
295  keyChar = str[0];
296  auto end = str.find_first_of(keyChar, 1);
297  while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes
298  end = str.find_first_of(keyChar, end + 1);
299  embeddedQuote = true;
300  }
301  if(end != std::string::npos) {
302  output.push_back(str.substr(1, end - 1));
303  str = str.substr(end + 1);
304  } else {
305  output.push_back(str.substr(1));
306  str = "";
307  }
308  } else {
309  auto it = std::find_if(std::begin(str), std::end(str), find_ws);
310  if(it != std::end(str)) {
311  std::string value = std::string(str.begin(), it);
312  output.push_back(value);
313  str = std::string(it + 1, str.end());
314  } else {
315  output.push_back(str);
316  str = "";
317  }
318  }
319  // transform any embedded quotes into the regular character
320  if(embeddedQuote) {
321  output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar));
322  embeddedQuote = false;
323  }
324  trim(str);
325  }
326  return output;
327 }
328 
333 inline std::string fix_newlines(const std::string &leader, std::string input) {
334  std::string::size_type n = 0;
335  while(n != std::string::npos && n < input.size()) {
336  n = input.find('\n', n);
337  if(n != std::string::npos) {
338  input = input.substr(0, n + 1) + leader + input.substr(n + 1);
339  n += leader.size();
340  }
341  }
342  return input;
343 }
344 
349 inline std::size_t escape_detect(std::string &str, std::size_t offset) {
350  auto next = str[offset + 1];
351  if((next == '\"') || (next == '\'') || (next == '`')) {
352  auto astart = str.find_last_of("-/ \"\'`", offset - 1);
353  if(astart != std::string::npos) {
354  if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
355  str[offset] = ' '; // interpret this as a space so the split_up works properly
356  }
357  }
358  return offset + 1;
359 }
360 
362 inline std::string &add_quotes_if_needed(std::string &str) {
363  if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) {
364  char quote = str.find('"') < str.find('\'') ? '\'' : '"';
365  if(str.find(' ') != std::string::npos) {
366  str.insert(0, 1, quote);
367  str.append(1, quote);
368  }
369  }
370  return str;
371 }
372 
373 } // namespace detail
374 
375 } // namespace CLI
CLI::detail::find_and_replace
std::string find_and_replace(std::string str, std::string from, std::string to)
Find and replace a substring with another substring.
Definition: StringTools.hpp:210
CLI::detail::valid_first_char
bool valid_first_char(T c)
Verify the first character of an option.
Definition: StringTools.hpp:173
CLI::detail::expected_max_vector_size
constexpr int expected_max_vector_size
Definition: StringTools.hpp:36
CLI::detail::rjoin
std::string rjoin(const T &v, std::string delim=",")
Join a string in reverse order.
Definition: StringTools.hpp:85
CLI::detail::split
std::vector< std::string > split(const std::string &s, char delim)
Split a string by a delim.
Definition: StringTools.hpp:39
CLI::detail::add_quotes_if_needed
std::string & add_quotes_if_needed(std::string &str)
Add quotes if the string contains spaces.
Definition: StringTools.hpp:362
CLI::detail::trim_copy
std::string trim_copy(const std::string &str)
Make a copy of the string and then trim it.
Definition: StringTools.hpp:133
CLI::detail::rtrim
std::string & rtrim(std::string &str)
Trim whitespace from right of string.
Definition: StringTools.hpp:112
CLI::detail::join
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:56
CLI::detail::valid_later_char
bool valid_later_char(T c)
Verify following characters of an option.
Definition: StringTools.hpp:178
CLI
Definition: App.hpp:27
CLI::detail::valid_name_string
bool valid_name_string(const std::string &str)
Verify an option name.
Definition: StringTools.hpp:181
CLI::detail::split_up
std::vector< std::string > split_up(std::string str, char delimiter='\0')
Definition: StringTools.hpp:282
CLI::detail::ltrim
std::string & ltrim(std::string &str)
Trim whitespace from left of string.
Definition: StringTools.hpp:98
CLI::ignore_case
std::string ignore_case(std::string item)
Helper function to allow ignore_case to be passed to IsMember or Transform.
Definition: Validators.hpp:890
CLI::enums::operator<<
std::ostream & operator<<(std::ostream &in, const T &item)
output streaming for enumerations
Definition: StringTools.hpp:23
CLI::detail::find_member
std::ptrdiff_t find_member(std::string name, const std::vector< std::string > names, bool ignore_case=false, bool ignore_underscore=false)
Check if a string is a member of a list of strings and optionally ignore case or ignore underscores.
Definition: StringTools.hpp:241
CLI::ignore_underscore
std::string ignore_underscore(std::string item)
Helper function to allow ignore_underscore to be passed to IsMember or Transform.
Definition: Validators.hpp:893
CLI::detail::find_and_modify
std::string find_and_modify(std::string str, std::string trigger, Callable modify)
Definition: StringTools.hpp:272
CLI::detail::remove_default_flag_values
void remove_default_flag_values(std::string &flags)
Definition: StringTools.hpp:227
CLI::detail::format_help
std::ostream & format_help(std::ostream &out, std::string name, std::string description, std::size_t wid)
Print a two part "help" string.
Definition: StringTools.hpp:155
CLI::detail::to_lower
std::string to_lower(std::string str)
Return a lower case version of a string.
Definition: StringTools.hpp:196
CLI::detail::fix_newlines
std::string fix_newlines(const std::string &leader, std::string input)
Definition: StringTools.hpp:333
CLI::detail::remove_underscore
std::string remove_underscore(std::string str)
remove underscores from a string
Definition: StringTools.hpp:204
CLI::detail::remove_quotes
std::string & remove_quotes(std::string &str)
remove quotes at the front and back of a string either '"' or '\''
Definition: StringTools.hpp:139
CLI::detail::trim
std::string & trim(std::string &str)
Trim whitespace from string.
Definition: StringTools.hpp:127
CLI::detail::escape_detect
std::size_t escape_detect(std::string &str, std::size_t offset)
Definition: StringTools.hpp:349
CLI::detail::isalpha
bool isalpha(const std::string &str)
Verify that str consists of letters only.
Definition: StringTools.hpp:191
CLI::detail::has_default_flag_values
bool has_default_flag_values(const std::string &flags)
check if the flag definitions has possible false flags
Definition: StringTools.hpp:223