Apache Mesos
strings.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_STRINGS_HPP__
14 #define __STOUT_STRINGS_HPP__
15 
16 #include <algorithm>
17 #include <cstring>
18 #include <map>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22 
23 #include "foreach.hpp"
24 #include "format.hpp"
25 #include "option.hpp"
26 #include "stringify.hpp"
27 
28 namespace strings {
29 
30 const std::string WHITESPACE = " \t\n\r";
31 
32 // Flags indicating how 'remove' or 'trim' should operate.
33 enum Mode
34 {
38 };
39 
40 
41 inline std::string remove(
42  const std::string& from,
43  const std::string& substring,
44  Mode mode = ANY)
45 {
46  std::string result = from;
47 
48  if (mode == PREFIX) {
49  if (from.find(substring) == 0) {
50  result = from.substr(substring.size());
51  }
52  } else if (mode == SUFFIX) {
53  if (from.rfind(substring) == from.size() - substring.size()) {
54  result = from.substr(0, from.size() - substring.size());
55  }
56  } else {
57  size_t index;
58  while ((index = result.find(substring)) != std::string::npos) {
59  result = result.erase(index, substring.size());
60  }
61  }
62 
63  return result;
64 }
65 
66 
67 inline std::string trim(
68  const std::string& from,
69  Mode mode = ANY,
70  const std::string& chars = WHITESPACE)
71 {
72  size_t start = 0;
73  Option<size_t> end = None();
74 
75  if (mode == ANY) {
76  start = from.find_first_not_of(chars);
77  end = from.find_last_not_of(chars);
78  } else if (mode == PREFIX) {
79  start = from.find_first_not_of(chars);
80  } else if (mode == SUFFIX) {
81  end = from.find_last_not_of(chars);
82  }
83 
84  // Bail early if 'from' contains only characters in 'chars'.
85  if (start == std::string::npos) {
86  return "";
87  }
88 
89  // Calculate the length of the substring, defaulting to the "end" of
90  // string if there were no characters to remove from the suffix.
91  size_t length = std::string::npos;
92 
93  // Found characters to trim at the end.
94  if (end.isSome() && end.get() != std::string::npos) {
95  length = end.get() + 1 - start;
96  }
97 
98  return from.substr(start, length);
99 }
100 
101 
102 // Helper providing some syntactic sugar for when 'mode' is ANY but
103 // the 'chars' are specified.
104 inline std::string trim(
105  const std::string& from,
106  const std::string& chars)
107 {
108  return trim(from, ANY, chars);
109 }
110 
111 
112 // Replaces all the occurrences of the 'from' string with the 'to' string.
113 inline std::string replace(
114  const std::string& s,
115  const std::string& from,
116  const std::string& to)
117 {
118  std::string result = s;
119  size_t index = 0;
120 
121  if (from.empty()) {
122  return result;
123  }
124 
125  while ((index = result.find(from, index)) != std::string::npos) {
126  result.replace(index, from.length(), to);
127  index += to.length();
128  }
129  return result;
130 }
131 
132 
133 // Tokenizes the string using the delimiters. Empty tokens will not be
134 // included in the result.
135 //
136 // Optionally, the maximum number of tokens to be returned can be
137 // specified. If the maximum number of tokens is reached, the last
138 // token returned contains the remainder of the input string.
139 inline std::vector<std::string> tokenize(
140  const std::string& s,
141  const std::string& delims,
142  const Option<size_t>& maxTokens = None())
143 {
144  if (maxTokens.isSome() && maxTokens.get() == 0) {
145  return {};
146  }
147 
148  std::vector<std::string> tokens;
149  size_t offset = 0;
150 
151  while (true) {
152  size_t nonDelim = s.find_first_not_of(delims, offset);
153 
154  if (nonDelim == std::string::npos) {
155  break; // Nothing left.
156  }
157 
158  size_t delim = s.find_first_of(delims, nonDelim);
159 
160  // Finish tokenizing if this is the last token,
161  // or we've found enough tokens.
162  if (delim == std::string::npos ||
163  (maxTokens.isSome() && tokens.size() == maxTokens.get() - 1)) {
164  tokens.push_back(s.substr(nonDelim));
165  break;
166  }
167 
168  tokens.push_back(s.substr(nonDelim, delim - nonDelim));
169  offset = delim;
170  }
171 
172  return tokens;
173 }
174 
175 
176 // Splits the string using the provided delimiters. The string is
177 // split each time at the first character that matches any of the
178 // characters specified in delims. Empty tokens are allowed in the
179 // result.
180 //
181 // Optionally, the maximum number of tokens to be returned can be
182 // specified. If the maximum number of tokens is reached, the last
183 // token returned contains the remainder of the input string.
184 inline std::vector<std::string> split(
185  const std::string& s,
186  const std::string& delims,
187  const Option<size_t>& maxTokens = None())
188 {
189  if (maxTokens.isSome() && maxTokens.get() == 0) {
190  return {};
191  }
192 
193  std::vector<std::string> tokens;
194  size_t offset = 0;
195 
196  while (true) {
197  size_t next = s.find_first_of(delims, offset);
198 
199  // Finish splitting if this is the last token,
200  // or we've found enough tokens.
201  if (next == std::string::npos ||
202  (maxTokens.isSome() && tokens.size() == maxTokens.get() - 1)) {
203  tokens.push_back(s.substr(offset));
204  break;
205  }
206 
207  tokens.push_back(s.substr(offset, next - offset));
208  offset = next + 1;
209  }
210 
211  return tokens;
212 }
213 
214 
215 // Returns a map of strings to strings based on calling tokenize
216 // twice. All non-pairs are discarded. For example:
217 //
218 // pairs("foo=1;bar=2;baz;foo=3;bam=1=2", ";&", "=")
219 //
220 // Would return a map with the following:
221 // bar: ["2"]
222 // foo: ["1", "3"]
223 inline std::map<std::string, std::vector<std::string>> pairs(
224  const std::string& s,
225  const std::string& delims1,
226  const std::string& delims2)
227 {
228  std::map<std::string, std::vector<std::string>> result;
229 
230  const std::vector<std::string> tokens = tokenize(s, delims1);
231  foreach (const std::string& token, tokens) {
232  const std::vector<std::string> pairs = tokenize(token, delims2);
233  if (pairs.size() == 2) {
234  result[pairs[0]].push_back(pairs[1]);
235  }
236  }
237 
238  return result;
239 }
240 
241 
242 namespace internal {
243 
244 inline std::stringstream& append(
245  std::stringstream& stream,
246  const std::string& value)
247 {
248  stream << value;
249  return stream;
250 }
251 
252 
253 inline std::stringstream& append(
254  std::stringstream& stream,
255  std::string&& value)
256 {
257  stream << value;
258  return stream;
259 }
260 
261 
262 inline std::stringstream& append(
263  std::stringstream& stream,
264  const char*&& value)
265 {
266  stream << value;
267  return stream;
268 }
269 
270 
271 template <typename T>
272 std::stringstream& append(
273  std::stringstream& stream,
274  T&& value)
275 {
276  stream << ::stringify(std::forward<T>(value));
277  return stream;
278 }
279 
280 
281 template <typename T>
282 std::stringstream& join(
283  std::stringstream& stream,
284  const std::string& separator,
285  T&& tail)
286 {
287  return append(stream, std::forward<T>(tail));
288 }
289 
290 
291 template <typename THead, typename... TTail>
292 std::stringstream& join(
293  std::stringstream& stream,
294  const std::string& separator,
295  THead&& head,
296  TTail&&... tail)
297 {
298  append(stream, std::forward<THead>(head)) << separator;
299  internal::join(stream, separator, std::forward<TTail>(tail)...);
300  return stream;
301 }
302 
303 } // namespace internal {
304 
305 
306 template <typename... T>
307 std::stringstream& join(
308  std::stringstream& stream,
309  const std::string& separator,
310  T&&... args)
311 {
312  internal::join(stream, separator, std::forward<T>(args)...);
313  return stream;
314 }
315 
316 
317 // Use 2 heads here to disambiguate variadic argument join from the
318 // templatized Iterable join below. This means this implementation of
319 // strings::join() is only activated if there are 2 or more things to
320 // join.
321 template <typename THead1, typename THead2, typename... TTail>
322 std::string join(
323  const std::string& separator,
324  THead1&& head1,
325  THead2&& head2,
326  TTail&&... tail)
327 {
328  std::stringstream stream;
330  stream,
331  separator,
332  std::forward<THead1>(head1),
333  std::forward<THead2>(head2),
334  std::forward<TTail>(tail)...);
335  return stream.str();
336 }
337 
338 
339 // Ensure std::string doesn't fall into the iterable case
340 inline std::string join(const std::string& seperator, const std::string& s) {
341  return s;
342 }
343 
344 
345 // Use duck-typing to join any iterable.
346 template <typename Iterable>
347 inline std::string join(const std::string& separator, const Iterable& i)
348 {
349  std::string result;
350  typename Iterable::const_iterator iterator = i.begin();
351  while (iterator != i.end()) {
352  result += stringify(*iterator);
353  if (++iterator != i.end()) {
354  result += separator;
355  }
356  }
357  return result;
358 }
359 
360 
362  const std::string& s,
363  const char openBracket,
364  const char closeBracket)
365 {
366  int count = 0;
367  for (size_t i = 0; i < s.length(); i++) {
368  if (s[i] == openBracket) {
369  count++;
370  } else if (s[i] == closeBracket) {
371  count--;
372  }
373  if (count < 0) {
374  return false;
375  }
376  }
377  return count == 0;
378 }
379 
380 
381 inline bool startsWith(const std::string& s, const std::string& prefix)
382 {
383  return s.size() >= prefix.size() &&
384  std::equal(prefix.begin(), prefix.end(), s.begin());
385 }
386 
387 
388 inline bool startsWith(const std::string& s, const char* prefix)
389 {
390  size_t len = ::strnlen(prefix, s.size() + 1);
391  return s.size() >= len &&
392  std::equal(s.begin(), s.begin() + len, prefix);
393 }
394 
395 
396 inline bool startsWith(const std::string& s, char c)
397 {
398  return !s.empty() && s.front() == c;
399 }
400 
401 
402 inline bool endsWith(const std::string& s, const std::string& suffix)
403 {
404  return s.size() >= suffix.size() &&
405  std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
406 }
407 
408 
409 inline bool endsWidth(const std::string& s, const char* suffix)
410 {
411  size_t len = ::strnlen(suffix, s.size() + 1);
412  return s.size() >= len &&
413  std::equal(s.end() - len, s.end(), suffix);
414 }
415 
416 
417 inline bool endsWith(const std::string& s, char c)
418 {
419  return !s.empty() && s.back() == c;
420 }
421 
422 
423 inline bool contains(const std::string& s, const std::string& substr)
424 {
425  return s.find(substr) != std::string::npos;
426 }
427 
428 
429 inline std::string lower(const std::string& s)
430 {
431  std::string result = s;
432  std::transform(result.begin(), result.end(), result.begin(), ::tolower);
433  return result;
434 }
435 
436 
437 inline std::string upper(const std::string& s)
438 {
439  std::string result = s;
440  std::transform(result.begin(), result.end(), result.begin(), ::toupper);
441  return result;
442 }
443 
444 } // namespace strings {
445 
446 #endif // __STOUT_STRINGS_HPP__
bool endsWith(const std::string &s, const std::string &suffix)
Definition: strings.hpp:402
Try< Nothing > equal(const SlaveInfo &previous, const SlaveInfo &current)
std::stringstream & append(std::stringstream &stream, const std::string &value)
Definition: strings.hpp:244
Definition: format.hpp:45
constexpr const char * prefix
Definition: os.hpp:96
bool endsWidth(const std::string &s, const char *suffix)
Definition: strings.hpp:409
Try< Nothing > start(const std::string &name)
Starts the slice with the given name (via &#39;systemctl start <name>&#39;).
std::map< std::string, std::vector< std::string > > pairs(const std::string &s, const std::string &delims1, const std::string &delims2)
Definition: strings.hpp:223
bool isSome() const
Definition: option.hpp:116
std::vector< std::string > tokenize(const std::string &s, const std::string &delims, const Option< size_t > &maxTokens=None())
Definition: strings.hpp:139
bool contains(const std::string &s, const std::string &substr)
Definition: strings.hpp:423
Definition: strings.hpp:36
std::stringstream & join(std::stringstream &stream, const std::string &separator, T &&tail)
Definition: strings.hpp:282
const std::string WHITESPACE
Definition: strings.hpp:30
const T & get() const &
Definition: option.hpp:119
std::string replace(const std::string &s, const std::string &from, const std::string &to)
Definition: strings.hpp:113
process::Future< Nothing > transform(process::Owned< Reader< T >> &&reader, const std::function< std::string(const T &)> &func, process::http::Pipe::Writer writer)
This is a helper function that reads records from a Reader, applies a transformation to the records a...
Definition: recordio.hpp:112
std::string upper(const std::string &s)
Definition: strings.hpp:437
Definition: none.hpp:27
Definition: attributes.hpp:24
Mode
Definition: strings.hpp:33
Try< mode_t > mode(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:168
std::vector< std::string > split(const std::string &s, const std::string &delims, const Option< size_t > &maxTokens=None())
Definition: strings.hpp:184
std::string stringify(int flags)
bool startsWith(const std::string &s, const std::string &prefix)
Definition: strings.hpp:381
Definition: strings.hpp:37
std::string lower(const std::string &s)
Definition: strings.hpp:429
std::string trim(const std::string &from, Mode mode=ANY, const std::string &chars=WHITESPACE)
Definition: strings.hpp:67
bool checkBracketsMatching(const std::string &s, const char openBracket, const char closeBracket)
Definition: strings.hpp:361
Definition: strings.hpp:35