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