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