1 /**************************************************************************
2 Copyright:
3 (C) 2008 - 2012 Alexander Shaduri <ashaduri 'at' gmail.com>
4 License: See LICENSE_zlib.txt file
5 ***************************************************************************/
6 /// \file
7 /// \author Alexander Shaduri
8 /// \ingroup hz
9 /// \weakgroup hz
10 /// @{
11
12 #ifndef HZ_STRING_ALGO_H
13 #define HZ_STRING_ALGO_H
14
15 #include "hz_config.h" // feature macros
16
17 #include <string>
18 #include <cctype> // std::tolower, std::toupper
19
20
21
22 namespace hz {
23
24
25
26 // --------------------------------------------- Split
27
28
29 /// Split a string into components by character (delimiter), appending the
30 /// components (without delimiters) to container "append_here".
31 /// If skip_empty is true, then empty components will be omitted.
32 /// If "limit" is more than 0, only put a maximum of "limit" number of
33 /// elements into vector, with the last one containing the rest of the string.
34 template<class Container> inline
35 void string_split(const std::string& str, char delimiter,
36 Container& append_here, bool skip_empty = false, typename Container::difference_type limit = 0)
37 {
38 std::string::size_type curr = 0, last = 0;
39 std::string::size_type end = str.size();
40 typename Container::difference_type num = 0; // number inserted
41
42 while (true) {
43 if (last >= end) { // last is past the end
44 if (!skip_empty) // no need to check num here
45 append_here.push_back(std::string());
46 break;
47 }
48
49 curr = str.find(delimiter, last);
50
51 if (!skip_empty || (curr != last)) {
52 if (++num == limit) {
53 append_here.push_back(str.substr(last, std::string::npos));
54 break;
55 } else {
56 append_here.push_back(str.substr(last, (curr == std::string::npos ? curr : (curr - last))));
57 }
58 }
59
60 if (curr == std::string::npos)
61 break;
62
63 last = curr + 1;
64 }
65 }
66
67
68
69 /// Split a string into components by another string (delimiter), appending the
70 /// components (without delimiters) to container "append_here".
71 /// If skip_empty is true, then empty components will be omitted.
72 /// If "limit" is more than 0, only put a maximum of "limit" number of
73 /// elements into vector, with the last one containing the rest of the string.
74 template<class Container> inline
75 void string_split(const std::string& str, const std::string& delimiter,
76 Container& append_here, bool skip_empty = false, typename Container::difference_type limit = 0)
77 {
78 std::string::size_type skip_size = delimiter.size();
79 if (str.size() < skip_size) {
80 append_here.push_back(str);
81 return;
82 }
83
84 std::string::size_type curr = 0, last = 0;
85 std::string::size_type end = str.size();
86 typename Container::difference_type num = 0; // number inserted
87
88 while (true) {
89 if (last >= end) { // last is past the end
90 if (!skip_empty) // no need to check num here
91 append_here.push_back(std::string());
92 break;
93 }
94
95 curr = str.find(delimiter, last);
96 std::string component(str, last, (curr == std::string::npos ? curr : (curr - last)));
97
98 if (!skip_empty || !component.empty()) {
99 if (++num == limit) {
100 append_here.push_back(str.substr(last, std::string::npos));
101 break;
102 } else {
103 append_here.push_back(component);
104 }
105 }
106
107 if (curr == std::string::npos)
108 break;
109
110 last = curr + skip_size;
111 }
112 }
113
114
115 /// Split a string into components by any of the characters (delimiters), appending the
116 /// components (without delimiters) to container "append_here".
117 /// If skip_empty is true, then empty components will be omitted.
118 /// If "limit" is more than 0, only put a maximum of "limit" number of
119 /// elements into vector, with the last one containing the rest of the string.
120 template<class Container> inline
121 void string_split_by_chars(const std::string& str, const std::string& delimiter_chars,
122 Container& append_here, bool skip_empty = false, typename Container::difference_type limit = 0)
123 {
124 std::string::size_type curr = 0, last = 0;
125 std::string::size_type end = str.size();
126 typename Container::difference_type num = 0; // number inserted
127
128 while (true) {
129 if (last >= end) { // last is past the end
130 if (!skip_empty) // no need to check num here
131 append_here.push_back(std::string());
132 break;
133 }
134
135 curr = str.find_first_of(delimiter_chars, last);
136
137 if (!skip_empty || (curr != last)) {
138 if (++num == limit) {
139 append_here.push_back(str.substr(last, std::string::npos));
140 break;
141 } else {
142 append_here.push_back(str.substr(last, (curr == std::string::npos ? curr : (curr - last))));
143 }
144 }
145
146 if (curr == std::string::npos)
147 break;
148
149 last = curr + 1;
150 }
151 }
152
153
154
155
156 // --------------------------------------------- Join
157
158
159 /// Join elements of container v into a string, using glue between them.
160 template<class Container> inline
string_join(const Container & v,char glue)161 std::string string_join(const Container& v, char glue)
162 {
163 typename Container::const_iterator begin = v.begin();
164 typename Container::const_iterator iter = begin;
165
166 std::string ret;
167 for (; iter != v.end(); ++iter) {
168 if (iter != begin)
169 ret += glue;
170 ret += (*iter);
171 }
172
173 return ret;
174 }
175
176
177
178 /// Join elements of container v into a string, using glue between them.
179 template<class Container> inline
string_join(const Container & v,const std::string & glue)180 std::string string_join(const Container& v, const std::string& glue)
181 {
182 typename Container::const_iterator begin = v.begin();
183 typename Container::const_iterator iter = begin;
184
185 std::string ret;
186 for (; iter != v.end(); ++iter) {
187 if (iter != begin)
188 ret += glue;
189 ret += (*iter);
190 }
191
192 return ret;
193 }
194
195
196
197
198 // --------------------------------------------- Trim
199
200
201 /// Trim a string s from both sides (modifying s). Return true if s was modified.
202 /// Trimming removes all trim_chars that occur on either side of the string s.
203 inline bool string_trim(std::string& s, const std::string& trim_chars = " \t\r\n")
204 {
205 if (trim_chars.empty())
206 return false;
207
208 const std::string::size_type s_size = s.size();
209
210 std::string::size_type index = s.find_last_not_of(trim_chars);
211 if (index != std::string::npos)
212 s.erase(index + 1); // from index+1 to the end
213
214 index = s.find_first_not_of(trim_chars);
215 if (index != std::string::npos)
216 s.erase(0, index);
217 else
218 s.clear();
219
220 return s_size != s.size(); // true if s was modified
221 }
222
223
224 /// Trim a string s from both sides (not modifying s), returning the changed string.
225 /// Trimming removes all trim_chars that occur on either side of the string s.
226 inline std::string string_trim_copy(const std::string& s, const std::string& trim_chars = " \t\r\n")
227 {
228 std::string ret(s);
229 string_trim(ret, trim_chars);
230 return ret;
231 }
232
233
234
235
236 /// Trim a string s from the left (modifying s). Return true if s was modified.
237 /// Trimming removes all trim_chars that occur on the left side of the string s.
238 inline bool string_trim_left(std::string& s, const std::string& trim_chars = " \t\r\n")
239 {
240 if (trim_chars.empty())
241 return false;
242
243 const std::string::size_type s_size = s.size();
244
245 std::string::size_type index = s.find_first_not_of(trim_chars);
246 if (index != std::string::npos)
247 s.erase(0, index);
248 else
249 s.clear();
250
251 return s_size != s.size(); // true if s was modified
252 }
253
254
255 /// Trim a string s from the left (not modifying s), returning the changed string.
256 /// Trimming removes all trim_chars that occur on the left side of the string s.
257 inline std::string string_trim_left_copy(const std::string& s, const std::string& trim_chars = " \t\r\n")
258 {
259 std::string ret(s);
260 string_trim_left(ret, trim_chars);
261 return ret;
262 }
263
264
265
266
267 /// Trim a string s from the right (modifying s). Return true if s was modified.
268 /// Trimming removes all trim_chars that occur on the right side of the string s.
269 inline bool string_trim_right(std::string& s, const std::string& trim_chars = " \t\r\n")
270 {
271 if (trim_chars.empty())
272 return false;
273
274 const std::string::size_type s_size = s.size();
275
276 std::string::size_type index = s.find_last_not_of(trim_chars);
277 if (index != std::string::npos)
278 s.erase(index + 1); // from index+1 to the end
279
280 return s_size != s.size(); // true if s was modified
281 }
282
283
284 /// Trim a string s from the right (not modifying s), returning the changed string.
285 /// Trimming removes all trim_chars that occur on the right side of the string s.
286 inline std::string string_trim_right_copy(const std::string& s, const std::string& trim_chars = " \t\r\n")
287 {
288 std::string ret(s);
289 string_trim_right(ret, trim_chars);
290 return ret;
291 }
292
293
294
295 // --------------------------------------------- Erase
296
297
298 /// Erase the left side of string s if it contains substring_to_erase,
299 /// modifying s. Returns true if s was modified.
string_erase_left(std::string & s,const std::string & substring_to_erase)300 inline bool string_erase_left(std::string& s, const std::string& substring_to_erase)
301 {
302 if (substring_to_erase.empty())
303 return false;
304
305 std::string::size_type sub_size = substring_to_erase.size();
306 if (s.compare(0, sub_size, substring_to_erase) == 0) {
307 s.erase(0, sub_size);
308 return true;
309 }
310
311 return false;
312 }
313
314
315 /// Erase the left side of string s if it contains substring_to_erase,
316 /// not modifying s, returning the changed string.
string_erase_left_copy(const std::string & s,const std::string & substring_to_erase)317 inline std::string string_erase_left_copy(const std::string& s, const std::string& substring_to_erase)
318 {
319 std::string ret(s);
320 string_erase_left(ret, substring_to_erase);
321 return ret;
322 }
323
324
325
326
327 /// Erase the right side of string s if it contains substring_to_erase,
328 /// modifying s. Returns true if s was modified.
string_erase_right(std::string & s,const std::string & substring_to_erase)329 inline bool string_erase_right(std::string& s, const std::string& substring_to_erase)
330 {
331 std::string::size_type sub_size = substring_to_erase.size();
332 if (sub_size == 0)
333 return false;
334
335 std::string::size_type s_size = s.size();
336 if (sub_size > s_size)
337 return false;
338
339 if (s.compare(s_size - sub_size, sub_size, substring_to_erase) == 0) {
340 s.erase(s_size - sub_size, sub_size);
341 return true;
342 }
343
344 return false;
345 }
346
347
348 /// Erase the right side of string s if it contains substring_to_erase,
349 /// not modifying s, returning the changed string.
string_erase_right_copy(const std::string & s,const std::string & substring_to_erase)350 inline std::string string_erase_right_copy(const std::string& s, const std::string& substring_to_erase)
351 {
352 std::string ret(s);
353 string_erase_right(ret, substring_to_erase);
354 return ret;
355 }
356
357
358
359 // --------------------------------------------- Misc. Transformations
360
361
362 /// remove adjacent duplicate chars inside s (modifying s).
363 /// returns true if s was modified.
364 /// useful for e.g. removing extra spaces inside the string.
365 inline bool string_remove_adjacent_duplicates(std::string& s, char c, unsigned int max_out_adjacent = 1)
366 {
367 if (s.size() <= max_out_adjacent)
368 return false;
369
370 bool changed = false;
371 std::string::size_type pos1 = 0, pos2;
372
373 while ((pos1 = s.find(c, pos1)) != std::string::npos) {
374 pos2 = s.find_first_not_of(c, pos1);
375 if (pos2 == std::string::npos)
376 pos2 = s.size(); // just past the last char
377 if (pos2 - pos1 > max_out_adjacent) {
378 s.erase(pos1 + max_out_adjacent, pos2 - pos1 - max_out_adjacent);
379 changed = true;
380 }
381 pos1 += max_out_adjacent;
382 }
383
384 return changed;
385 }
386
387
388 /// remove adjacent duplicate chars inside s, not modifying s, returning the changed string.
389 inline std::string string_remove_adjacent_duplicates_copy(const std::string& s, char c, unsigned int max_out_adjacent = 1)
390 {
391 std::string ret(s);
392 string_remove_adjacent_duplicates(ret, c, max_out_adjacent);
393 return ret;
394 }
395
396
397
398
399 // --------------------------------------------- Replace
400
401
402 // TODO: Add string_replace_linear(), where multiple strings are replaced into the
403 // original string (as opposed to previous result).
404
405
406 /// Replace from with to inside s (modifying s). Return number of replacements made.
407 inline std::string::size_type string_replace(std::string& s,
408 const std::string& from, const std::string& to, int max_replacements = -1)
409 {
410 if (from.empty())
411 return std::string::npos;
412 if (max_replacements == 0 || from == to)
413 return 0;
414
415 const std::string::size_type from_len(from.size());
416 const std::string::size_type to_len(to.size());
417
418 std::string::size_type cnt = 0;
419 std::string::size_type pos = 0;
420
421 while ((pos = s.find(from, pos)) != std::string::npos) {
422 s.replace(pos, from_len, to);
423 pos += to_len;
424 if (static_cast<int>(++cnt) >= max_replacements && max_replacements != -1)
425 break;
426 }
427
428 return cnt;
429 }
430
431
432 /// Replace from with to inside s, not modifying s, returning the changed string.
433 inline std::string string_replace_copy(const std::string& s,
434 const std::string& from, const std::string& to, int max_replacements = -1)
435 {
436 std::string ret(s);
437 string_replace(ret, from, to, max_replacements);
438 return ret;
439 }
440
441
442
443
444 /// Replace from with to inside s (modifying s). char version.
445 inline std::string::size_type string_replace(std::string& s,
446 char from, char to, int max_replacements = -1)
447 {
448 if (max_replacements == 0 || from == to)
449 return 0;
450
451 std::string::size_type cnt = 0, pos = 0;
452
453 while ((pos = s.find(from, pos)) != std::string::npos) {
454 s[pos] = to;
455 ++pos;
456 if (static_cast<int>(++cnt) >= max_replacements && max_replacements != -1)
457 break;
458 }
459
460 return cnt;
461 }
462
463
464 /// Replace from with to inside s, not modifying s, returning the changed string. char version.
465 inline std::string string_replace_copy(const std::string& s,
466 char from, char to, int max_replacements = -1)
467 {
468 std::string ret(s);
469 string_replace(ret, from, to);
470 return ret;
471 }
472
473
474
475
476
477 /// Replace from_chars[0] with to_chars[0], from_chars[1] with to_chars[1], etc... in s (modifying s).
478 /// from_chars.size() must be equal to to_chars.size().
479 /// Note: This is a multi-pass algorithm (there are from_chars.size() iterations).
480 inline std::string::size_type string_replace_chars(std::string& s,
481 const std::string& from_chars, const std::string& to_chars, int max_replacements = -1)
482 {
483 const std::string::size_type from_size = from_chars.size();
484 if (from_size != to_chars.size())
485 return std::string::npos;
486 if (max_replacements == 0 || from_chars == to_chars)
487 return 0;
488
489 std::string::size_type cnt = 0, pos = 0;
490
491 for(std::string::size_type i = 0; i < from_size; ++i) {
492 pos = 0;
493 char from = from_chars[i], to = to_chars[i];
494 while ((pos = s.find(from, pos)) != std::string::npos) {
495 s[pos] = to;
496 ++pos;
497 if (static_cast<int>(++cnt) >= max_replacements && max_replacements != -1)
498 break;
499 }
500 if (max_replacements != -1 && static_cast<int>(cnt) >= max_replacements)
501 break;
502 }
503
504 return cnt;
505 }
506
507
508 /// Replace from_chars[0] with to_chars[0], from_chars[1] with to_chars[1], etc... in s,
509 /// not modifying s, returning the changed string.
510 /// from_chars.size() must be equal to to_chars.size().
511 /// Note: This is a multi-pass algorithm (there are from_chars.size() iterations).
512 inline std::string string_replace_chars_copy(const std::string& s,
513 const std::string& from_chars, const std::string& to_chars, int max_replacements = -1)
514 {
515 std::string ret(s);
516 string_replace_chars(ret, from_chars, to_chars);
517 return ret;
518 }
519
520
521
522
523
524 /// Replace all chars from from_chars with to_char (modifying s).
525 inline std::string::size_type string_replace_chars(std::string& s,
526 const std::string& from_chars, char to_char, int max_replacements = -1)
527 {
528 if (from_chars.empty())
529 return std::string::npos;
530 if (max_replacements == 0)
531 return 0;
532
533 std::string::size_type cnt = 0, pos = 0;
534 while ((pos = s.find_first_of(from_chars, pos)) != std::string::npos) {
535 s[pos] = to_char;
536 ++pos;
537 if (static_cast<int>(++cnt) >= max_replacements && max_replacements != -1)
538 break;
539 }
540
541 return cnt;
542 }
543
544
545 /// Replace all chars from from_chars with to_char, not modifying s, returning the changed string.
546 inline std::string string_replace_chars_copy(const std::string& s,
547 const std::string& from_chars, char to_char, int max_replacements = -1)
548 {
549 std::string ret(s);
550 string_replace_chars(ret, from_chars, to_char);
551 return ret;
552 }
553
554
555
556
557
558 /// Replace from_strings[0] with to_strings[0], from_strings[1] with to_strings[1], etc...
559 /// in s (modifying s). Returns total number of replacements performed.
560 /// from_strings.size() must be equal to to_strings.size().
561 /// Note: This is a multi-pass algorithm (there are from_strings.size() iterations).
562
563 /// Implementation note: We cannot use "template<template<class> C1>", because
564 /// it appears that it was a gcc extension, removed in 4.1 (C1 cannot bind to std::vector,
565 /// which has 2 (or more, implementation dependent) template parameters. gcc 4.1
566 /// allowed this because vector's other parameters have defaults).
567 template<class Container1, class Container2> inline
568 std::string::size_type string_replace_array(std::string& s,
569 const Container1& from_strings, const Container2& to_strings, int max_replacements = -1)
570 {
571 const std::string::size_type from_array_size = from_strings.size();
572
573 if (from_array_size != to_strings.size())
574 return std::string::npos;
575 if (max_replacements == 0)
576 return 0;
577 // if (from_strings == to_strings) // don't check this, it's too expensive. it will work anyway.
578 // return 0;
579
580 std::string::size_type cnt = 0, pos = 0;
581
582 for(std::string::size_type i = 0; i < from_array_size; ++i) {
583 pos = 0;
584 while ((pos = s.find(from_strings[i], pos)) != std::string::npos) {
585 s.replace(pos, from_strings[i].size(), to_strings[i]);
586 pos += to_strings[i].size();
587 if (static_cast<int>(++cnt) >= max_replacements && max_replacements != -1)
588 break;
589 }
590 if (max_replacements != -1 && static_cast<int>(cnt) > max_replacements)
591 break;
592 }
593
594 return cnt;
595 }
596
597
598 /// Eeplace from_strings[0] with to_strings[0], from_strings[1] with to_strings[1], etc... in s,
599 /// not modifying s, returning the changed string.
600 /// from_strings.size() must be equal to to_strings.size().
601 /// Note: This is a multi-pass algorithm (there are from_strings.size() iterations).
602 template<class Container1, class Container2> inline
603 std::string string_replace_array_copy(const std::string& s,
604 const Container1& from_strings, const Container2& to_strings, int max_replacements = -1)
605 {
606 std::string ret(s);
607 string_replace_array(ret, from_strings, to_strings);
608 return ret;
609 }
610
611
612
613
614
615 /// Replace all strings in from_strings with to_string in s (modifying s).
616 /// Returns total number of replacements performed.
617 /// Note: This is a one-pass algorithm.
618 template<class Container> inline
619 std::string::size_type string_replace_array(std::string& s,
620 const Container& from_strings, const std::string& to_string, int max_replacements = -1)
621 {
622 const std::string::size_type from_array_size = from_strings.size();
623 const std::string::size_type to_str_size = to_string.size();
624
625 if (from_array_size == 0)
626 return std::string::npos;
627 if (max_replacements == 0)
628 return 0;
629
630 std::string::size_type cnt = 0, pos = 0;
631 for(std::string::size_type i = 0; i < from_array_size; ++i) {
632 pos = 0;
633 while ((pos = s.find(from_strings[i], pos)) != std::string::npos) {
634 s.replace(pos, from_strings[i].size(), to_string);
635 pos += to_str_size;
636 if (static_cast<int>(++cnt) >= max_replacements && max_replacements != -1)
637 break;
638 }
639 if (max_replacements != -1 && static_cast<int>(cnt) > max_replacements)
640 break;
641 }
642
643 return cnt;
644 }
645
646
647 /// Replace all strings in from_strings with to_string in s, not modifying s, returning the changed string.
648 /// Note: This is a one-pass algorithm.
649 template<class Container> inline
650 std::string string_replace_array_copy(const std::string& s,
651 const Container& from_strings, const std::string& to_string, int max_replacements = -1)
652 {
653 std::string ret(s);
654 string_replace_array(ret, from_strings, to_string);
655 return ret;
656 }
657
658
659
660
661 /// Same as the other overloads, but needed to avoid conflict with all-template version
662 template<class Container> inline
663 std::string::size_type string_replace_array(std::string& s,
664 const Container& from_strings, const char* to_string, int max_replacements = -1)
665 {
666 return string_replace_array<Container>(s, from_strings, std::string(to_string), max_replacements);
667 }
668
669
670 // Same as the other overloads, but needed to avoid conflict with all-template version
671 template<class Container> inline
672 std::string string_replace_array_copy(const std::string& s,
673 const Container& from_strings, const char* to_string, int max_replacements = -1)
674 {
675 return string_replace_array_copy<Container>(s, from_strings, std::string(to_string), max_replacements);
676 }
677
678
679
680
681 // --------------------------------------------- Matching
682
683
684
685 /// Check whether a string begins with another string
string_begins_with(const std::string & str,const std::string & substr)686 inline bool string_begins_with(const std::string& str, const std::string& substr)
687 {
688 if (str.length() >= substr.length()) {
689 return (str.compare(0, substr.length(), substr) == 0);
690 }
691 return false;
692 }
693
694
695
696 /// Check whether a string begins with a character
string_begins_with(const std::string & str,char ch)697 inline bool string_begins_with(const std::string& str, char ch)
698 {
699 return !str.empty() && str[0] == ch;
700 }
701
702
703
704 /// Check whether a string ends with another string
string_ends_with(const std::string & str,const std::string & substr)705 inline bool string_ends_with(const std::string& str, const std::string& substr)
706 {
707 if (str.length() >= substr.length()) {
708 return (str.compare(str.length() - substr.length(), substr.length(), substr) == 0);
709 }
710 return false;
711 }
712
713
714
715 /// Check whether a string ends with a character
string_ends_with(const std::string & str,char ch)716 inline bool string_ends_with(const std::string& str, char ch)
717 {
718 return !str.empty() && str[str.size() - 1] == ch;
719 }
720
721
722
723
724 // --------------------------------------------- Utility
725
726
727 /// Auto-detect and convert mac/dos/unix newline formats in s (modifying s) to unix format.
728 /// Returns true if \c s was changed.
string_any_to_unix(std::string & s)729 inline bool string_any_to_unix(std::string& s)
730 {
731 std::string::size_type n = hz::string_replace(s, "\r\n", "\n"); // dos
732 n += hz::string_replace(s, '\r', '\n'); // mac
733 return bool(n);
734 }
735
736
737
738 /// Auto-detect and convert mac/dos/unix newline formats in s to unix format.
739 /// Returns the result string.
string_any_to_unix_copy(const std::string & s)740 inline std::string string_any_to_unix_copy(const std::string& s)
741 {
742 std::string ret(s);
743 string_any_to_unix(ret);
744 return ret;
745 }
746
747
748 /// Auto-detect and convert mac/dos/unix newline formats in s (modifying s) to dos format.
749 /// Returns true if \c s was changed.
string_any_to_dos(std::string & s)750 inline bool string_any_to_dos(std::string& s)
751 {
752 bool changed = string_any_to_unix(s);
753 std::string::size_type n = hz::string_replace(s, "\n", "\r\n"); // dos
754 return bool(n) || changed; // may not really work
755 }
756
757
758
759 /// Auto-detect and convert mac/dos/unix newline formats in s to dos format.
760 /// Returns the result string.
string_any_to_dos_copy(const std::string & s)761 inline std::string string_any_to_dos_copy(const std::string& s)
762 {
763 std::string ret(s);
764 string_any_to_dos(ret);
765 return ret;
766 }
767
768
769
770 /// Convert s to lowercase (modifying s). Return size of the string.
string_to_lower(std::string & s)771 inline std::string::size_type string_to_lower(std::string& s)
772 {
773 const std::string::size_type len = s.size();
774 for(std::string::size_type i = 0; i != len; ++i) {
775 s[i] = static_cast<char>(std::tolower(s[i]));
776 }
777 return len;
778 }
779
780
781
782 /// Convert s to lowercase, not modifying s, returning the changed string.
string_to_lower_copy(const std::string & s)783 inline std::string string_to_lower_copy(const std::string& s)
784 {
785 std::string ret(s);
786 string_to_lower(ret);
787 return ret;
788 }
789
790
791
792 /// Convert s to uppercase (modifying s). Return size of the string.
string_to_upper(std::string & s)793 inline std::string::size_type string_to_upper(std::string& s)
794 {
795 const std::string::size_type len = s.size();
796 for(std::string::size_type i = 0; i != len; ++i) {
797 s[i] = static_cast<char>(std::toupper(s[i]));
798 }
799 return len;
800 }
801
802
803
804 /// Convert s to uppercase, not modifying s, returning the changed string.
string_to_upper_copy(const std::string & s)805 inline std::string string_to_upper_copy(const std::string& s)
806 {
807 std::string ret(s);
808 string_to_upper(ret);
809 return ret;
810 }
811
812
813
814
815
816 } // ns
817
818
819
820
821 #endif
822
823 /// @}
824