1//
2// String.h
3//
4// Library: Foundation
5// Package: Core
6// Module: String
7//
8// String utility functions.
9//
10// Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
11// and Contributors.
12//
13// SPDX-License-Identifier: BSL-1.0
14//
15
16
17#ifndef Foundation_String_INCLUDED
18#define Foundation_String_INCLUDED
19
20
21#include "Poco/Foundation.h"
22#include "Poco/Ascii.h"
23#include <cstring>
24#include <algorithm>
25
26
27namespace Poco {
28
29
30template <class S>
31S trimLeft(const S& str)
32 /// Returns a copy of str with all leading
33 /// whitespace removed.
34{
35 typename S::const_iterator it = str.begin();
36 typename S::const_iterator end = str.end();
37
38 while (it != end && Ascii::isSpace(*it)) ++it;
39 return S(it, end);
40}
41
42
43template <class S>
44S& trimLeftInPlace(S& str)
45 /// Removes all leading whitespace in str.
46{
47 typename S::iterator it = str.begin();
48 typename S::iterator end = str.end();
49
50 while (it != end && Ascii::isSpace(*it)) ++it;
51 str.erase(str.begin(), it);
52 return str;
53}
54
55
56template <class S>
57S trimRight(const S& str)
58 /// Returns a copy of str with all trailing
59 /// whitespace removed.
60{
61 int pos = int(str.size()) - 1;
62
63 while (pos >= 0 && Ascii::isSpace(str[pos])) --pos;
64 return S(str, 0, pos + 1);
65}
66
67
68template <class S>
69S& trimRightInPlace(S& str)
70 /// Removes all trailing whitespace in str.
71{
72 int pos = int(str.size()) - 1;
73
74 while (pos >= 0 && Ascii::isSpace(str[pos])) --pos;
75 str.resize(pos + 1);
76
77 return str;
78}
79
80
81template <class S>
82S trim(const S& str)
83 /// Returns a copy of str with all leading and
84 /// trailing whitespace removed.
85{
86 int first = 0;
87 int last = int(str.size()) - 1;
88
89 while (first <= last && Ascii::isSpace(str[first])) ++first;
90 while (last >= first && Ascii::isSpace(str[last])) --last;
91
92 return S(str, first, last - first + 1);
93}
94
95
96template <class S>
97S& trimInPlace(S& str)
98 /// Removes all leading and trailing whitespace in str.
99{
100 int first = 0;
101 int last = int(str.size()) - 1;
102
103 while (first <= last && Ascii::isSpace(str[first])) ++first;
104 while (last >= first && Ascii::isSpace(str[last])) --last;
105
106 str.resize(last + 1);
107 str.erase(0, first);
108
109 return str;
110}
111
112
113template <class S>
114S toUpper(const S& str)
115 /// Returns a copy of str containing all upper-case characters.
116{
117 typename S::const_iterator it = str.begin();
118 typename S::const_iterator end = str.end();
119
120 S result;
121 result.reserve(str.size());
122 while (it != end) result += static_cast<typename S::value_type>(Ascii::toUpper(*it++));
123 return result;
124}
125
126
127template <class S>
128S& toUpperInPlace(S& str)
129 /// Replaces all characters in str with their upper-case counterparts.
130{
131 typename S::iterator it = str.begin();
132 typename S::iterator end = str.end();
133
134 while (it != end) { *it = static_cast<typename S::value_type>(Ascii::toUpper(*it)); ++it; }
135 return str;
136}
137
138
139template <class S>
140S toLower(const S& str)
141 /// Returns a copy of str containing all lower-case characters.
142{
143 typename S::const_iterator it = str.begin();
144 typename S::const_iterator end = str.end();
145
146 S result;
147 result.reserve(str.size());
148 while (it != end) result += static_cast<typename S::value_type>(Ascii::toLower(*it++));
149 return result;
150}
151
152
153template <class S>
154S& toLowerInPlace(S& str)
155 /// Replaces all characters in str with their lower-case counterparts.
156{
157 typename S::iterator it = str.begin();
158 typename S::iterator end = str.end();
159
160 while (it != end) { *it = static_cast<typename S::value_type>(Ascii::toLower(*it)); ++it; }
161 return str;
162}
163
164
165#if !defined(POCO_NO_TEMPLATE_ICOMPARE)
166
167
168template <class S, class It>
169int icompare(
170 const S& str,
171 typename S::size_type pos,
172 typename S::size_type n,
173 It it2,
174 It end2)
175 /// Case-insensitive string comparison
176{
177 typename S::size_type sz = str.size();
178 if (pos > sz) pos = sz;
179 if (pos + n > sz) n = sz - pos;
180 It it1 = str.begin() + pos;
181 It end1 = str.begin() + pos + n;
182 while (it1 != end1 && it2 != end2)
183 {
184 typename S::value_type c1(static_cast<typename S::value_type>(Ascii::toLower(*it1)));
185 typename S::value_type c2(static_cast<typename S::value_type>(Ascii::toLower(*it2)));
186 if (c1 < c2)
187 return -1;
188 else if (c1 > c2)
189 return 1;
190 ++it1; ++it2;
191 }
192
193 if (it1 == end1)
194 return it2 == end2 ? 0 : -1;
195 else
196 return 1;
197}
198
199
200template <class S>
201int icompare(const S& str1, const S& str2)
202 // A special optimization for an often used case.
203{
204 typename S::const_iterator it1(str1.begin());
205 typename S::const_iterator end1(str1.end());
206 typename S::const_iterator it2(str2.begin());
207 typename S::const_iterator end2(str2.end());
208 while (it1 != end1 && it2 != end2)
209 {
210 typename S::value_type c1(static_cast<typename S::value_type>(Ascii::toLower(*it1)));
211 typename S::value_type c2(static_cast<typename S::value_type>(Ascii::toLower(*it2)));
212 if (c1 < c2)
213 return -1;
214 else if (c1 > c2)
215 return 1;
216 ++it1; ++it2;
217 }
218
219 if (it1 == end1)
220 return it2 == end2 ? 0 : -1;
221 else
222 return 1;
223}
224
225
226template <class S>
227int icompare(const S& str1, typename S::size_type n1, const S& str2, typename S::size_type n2)
228{
229 if (n2 > str2.size()) n2 = str2.size();
230 return icompare(str1, 0, n1, str2.begin(), str2.begin() + n2);
231}
232
233
234template <class S>
235int icompare(const S& str1, typename S::size_type n, const S& str2)
236{
237 if (n > str2.size()) n = str2.size();
238 return icompare(str1, 0, n, str2.begin(), str2.begin() + n);
239}
240
241
242template <class S>
243int icompare(const S& str1, typename S::size_type pos, typename S::size_type n, const S& str2)
244{
245 return icompare(str1, pos, n, str2.begin(), str2.end());
246}
247
248
249template <class S>
250int icompare(
251 const S& str1,
252 typename S::size_type pos1,
253 typename S::size_type n1,
254 const S& str2,
255 typename S::size_type pos2,
256 typename S::size_type n2)
257{
258 typename S::size_type sz2 = str2.size();
259 if (pos2 > sz2) pos2 = sz2;
260 if (pos2 + n2 > sz2) n2 = sz2 - pos2;
261 return icompare(str1, pos1, n1, str2.begin() + pos2, str2.begin() + pos2 + n2);
262}
263
264
265template <class S>
266int icompare(
267 const S& str1,
268 typename S::size_type pos1,
269 typename S::size_type n,
270 const S& str2,
271 typename S::size_type pos2)
272{
273 typename S::size_type sz2 = str2.size();
274 if (pos2 > sz2) pos2 = sz2;
275 if (pos2 + n > sz2) n = sz2 - pos2;
276 return icompare(str1, pos1, n, str2.begin() + pos2, str2.begin() + pos2 + n);
277}
278
279
280template <class S>
281int icompare(
282 const S& str,
283 typename S::size_type pos,
284 typename S::size_type n,
285 const typename S::value_type* ptr)
286{
287 poco_check_ptr (ptr);
288 typename S::size_type sz = str.size();
289 if (pos > sz) pos = sz;
290 if (pos + n > sz) n = sz - pos;
291 typename S::const_iterator it = str.begin() + pos;
292 typename S::const_iterator end = str.begin() + pos + n;
293 while (it != end && *ptr)
294 {
295 typename S::value_type c1(static_cast<typename S::value_type>(Ascii::toLower(*it)));
296 typename S::value_type c2(static_cast<typename S::value_type>(Ascii::toLower(*ptr)));
297 if (c1 < c2)
298 return -1;
299 else if (c1 > c2)
300 return 1;
301 ++it; ++ptr;
302 }
303
304 if (it == end)
305 return *ptr == 0 ? 0 : -1;
306 else
307 return 1;
308}
309
310
311template <class S>
312int icompare(
313 const S& str,
314 typename S::size_type pos,
315 const typename S::value_type* ptr)
316{
317 return icompare(str, pos, str.size() - pos, ptr);
318}
319
320
321template <class S>
322int icompare(
323 const S& str,
324 const typename S::value_type* ptr)
325{
326 return icompare(str, 0, str.size(), ptr);
327}
328
329
330#else
331
332
333int Foundation_API icompare(const std::string& str, std::string::size_type pos, std::string::size_type n, std::string::const_iterator it2, std::string::const_iterator end2);
334int Foundation_API icompare(const std::string& str1, const std::string& str2);
335int Foundation_API icompare(const std::string& str1, std::string::size_type n1, const std::string& str2, std::string::size_type n2);
336int Foundation_API icompare(const std::string& str1, std::string::size_type n, const std::string& str2);
337int Foundation_API icompare(const std::string& str1, std::string::size_type pos, std::string::size_type n, const std::string& str2);
338int Foundation_API icompare(const std::string& str1, std::string::size_type pos1, std::string::size_type n1, const std::string& str2, std::string::size_type pos2, std::string::size_type n2);
339int Foundation_API icompare(const std::string& str1, std::string::size_type pos1, std::string::size_type n, const std::string& str2, std::string::size_type pos2);
340int Foundation_API icompare(const std::string& str, std::string::size_type pos, std::string::size_type n, const std::string::value_type* ptr);
341int Foundation_API icompare(const std::string& str, std::string::size_type pos, const std::string::value_type* ptr);
342int Foundation_API icompare(const std::string& str, const std::string::value_type* ptr);
343
344
345#endif
346
347
348template <class S>
349S translate(const S& str, const S& from, const S& to)
350 /// Returns a copy of str with all characters in
351 /// from replaced by the corresponding (by position)
352 /// characters in to. If there is no corresponding
353 /// character in to, the character is removed from
354 /// the copy.
355{
356 S result;
357 result.reserve(str.size());
358 typename S::const_iterator it = str.begin();
359 typename S::const_iterator end = str.end();
360 typename S::size_type toSize = to.size();
361 while (it != end)
362 {
363 typename S::size_type pos = from.find(*it);
364 if (pos == S::npos)
365 {
366 result += *it;
367 }
368 else
369 {
370 if (pos < toSize) result += to[pos];
371 }
372 ++it;
373 }
374 return result;
375}
376
377
378template <class S>
379S translate(const S& str, const typename S::value_type* from, const typename S::value_type* to)
380{
381 poco_check_ptr (from);
382 poco_check_ptr (to);
383 return translate(str, S(from), S(to));
384}
385
386
387template <class S>
388S& translateInPlace(S& str, const S& from, const S& to)
389 /// Replaces in str all occurrences of characters in from
390 /// with the corresponding (by position) characters in to.
391 /// If there is no corresponding character, the character
392 /// is removed.
393{
394 str = translate(str, from, to);
395 return str;
396}
397
398
399template <class S>
400S translateInPlace(S& str, const typename S::value_type* from, const typename S::value_type* to)
401{
402 poco_check_ptr (from);
403 poco_check_ptr (to);
404 str = translate(str, S(from), S(to));
405#if defined(__SUNPRO_CC)
406 // Fix around the RVO bug in SunStudio 12.4
407 S ret(str);
408 return ret;
409#else
410 return str;
411#endif
412}
413
414
415#if !defined(POCO_NO_TEMPLATE_ICOMPARE)
416
417
418template <class S>
419S& replaceInPlace(S& str, const S& from, const S& to, typename S::size_type start = 0)
420{
421 poco_assert (from.size() > 0);
422
423 S result;
424 typename S::size_type pos = 0;
425 result.append(str, 0, start);
426 do
427 {
428 pos = str.find(from, start);
429 if (pos != S::npos)
430 {
431 result.append(str, start, pos - start);
432 result.append(to);
433 start = pos + from.length();
434 }
435 else result.append(str, start, str.size() - start);
436 }
437 while (pos != S::npos);
438 str.swap(result);
439 return str;
440}
441
442
443template <class S>
444S& replaceInPlace(S& str, const typename S::value_type* from, const typename S::value_type* to, typename S::size_type start = 0)
445{
446 poco_assert (*from);
447
448 S result;
449 typename S::size_type pos = 0;
450 typename S::size_type fromLen = std::strlen(from);
451 result.append(str, 0, start);
452 do
453 {
454 pos = str.find(from, start);
455 if (pos != S::npos)
456 {
457 result.append(str, start, pos - start);
458 result.append(to);
459 start = pos + fromLen;
460 }
461 else result.append(str, start, str.size() - start);
462 }
463 while (pos != S::npos);
464 str.swap(result);
465 return str;
466}
467
468
469template <class S>
470S& replaceInPlace(S& str, const typename S::value_type from, const typename S::value_type to = 0, typename S::size_type start = 0)
471{
472 if (from == to) return str;
473
474 typename S::size_type pos = 0;
475 do
476 {
477 pos = str.find(from, start);
478 if (pos != S::npos)
479 {
480 if (to) str[pos] = to;
481 else str.erase(pos, 1);
482 }
483 } while (pos != S::npos);
484
485 return str;
486}
487
488
489template <class S>
490S& removeInPlace(S& str, const typename S::value_type ch, typename S::size_type start = 0)
491{
492 return replaceInPlace(str, ch, 0, start);
493}
494
495
496template <class S>
497S replace(const S& str, const S& from, const S& to, typename S::size_type start = 0)
498 /// Replace all occurrences of from (which must not be the empty string)
499 /// in str with to, starting at position start.
500{
501 S result(str);
502 replaceInPlace(result, from, to, start);
503 return result;
504}
505
506
507template <class S>
508S replace(const S& str, const typename S::value_type* from, const typename S::value_type* to, typename S::size_type start = 0)
509{
510 S result(str);
511 replaceInPlace(result, from, to, start);
512 return result;
513}
514
515
516template <class S>
517S replace(const S& str, const typename S::value_type from, const typename S::value_type to = 0, typename S::size_type start = 0)
518{
519 S result(str);
520 replaceInPlace(result, from, to, start);
521 return result;
522}
523
524
525template <class S>
526S remove(const S& str, const typename S::value_type ch, typename S::size_type start = 0)
527{
528 S result(str);
529 replaceInPlace(result, ch, 0, start);
530 return result;
531}
532
533
534#else
535
536
537Foundation_API std::string replace(const std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0);
538Foundation_API std::string replace(const std::string& str, const std::string::value_type* from, const std::string::value_type* to, std::string::size_type start = 0);
539Foundation_API std::string replace(const std::string& str, const std::string::value_type from, const std::string::value_type to = 0, std::string::size_type start = 0);
540Foundation_API std::string remove(const std::string& str, const std::string::value_type ch, std::string::size_type start = 0);
541Foundation_API std::string& replaceInPlace(std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0);
542Foundation_API std::string& replaceInPlace(std::string& str, const std::string::value_type* from, const std::string::value_type* to, std::string::size_type start = 0);
543Foundation_API std::string& replaceInPlace(std::string& str, const std::string::value_type from, const std::string::value_type to = 0, std::string::size_type start = 0);
544Foundation_API std::string& removeInPlace(std::string& str, const std::string::value_type ch, std::string::size_type start = 0);
545
546
547#endif
548
549
550template <class S>
551S cat(const S& s1, const S& s2)
552 /// Concatenates two strings.
553{
554 S result = s1;
555 result.reserve(s1.size() + s2.size());
556 result.append(s2);
557 return result;
558}
559
560
561template <class S>
562S cat(const S& s1, const S& s2, const S& s3)
563 /// Concatenates three strings.
564{
565 S result = s1;
566 result.reserve(s1.size() + s2.size() + s3.size());
567 result.append(s2);
568 result.append(s3);
569 return result;
570}
571
572
573template <class S>
574S cat(const S& s1, const S& s2, const S& s3, const S& s4)
575 /// Concatenates four strings.
576{
577 S result = s1;
578 result.reserve(s1.size() + s2.size() + s3.size() + s4.size());
579 result.append(s2);
580 result.append(s3);
581 result.append(s4);
582 return result;
583}
584
585
586template <class S>
587S cat(const S& s1, const S& s2, const S& s3, const S& s4, const S& s5)
588 /// Concatenates five strings.
589{
590 S result = s1;
591 result.reserve(s1.size() + s2.size() + s3.size() + s4.size() + s5.size());
592 result.append(s2);
593 result.append(s3);
594 result.append(s4);
595 result.append(s5);
596 return result;
597}
598
599
600template <class S>
601S cat(const S& s1, const S& s2, const S& s3, const S& s4, const S& s5, const S& s6)
602 /// Concatenates six strings.
603{
604 S result = s1;
605 result.reserve(s1.size() + s2.size() + s3.size() + s4.size() + s5.size() + s6.size());
606 result.append(s2);
607 result.append(s3);
608 result.append(s4);
609 result.append(s5);
610 result.append(s6);
611 return result;
612}
613
614
615template <class S, class It>
616S cat(const S& delim, const It& begin, const It& end)
617 /// Concatenates a sequence of strings, delimited
618 /// by the string given in delim.
619{
620 S result;
621 for (It it = begin; it != end; ++it)
622 {
623 if (!result.empty()) result.append(delim);
624 result += *it;
625 }
626 return result;
627}
628
629
630template <class S>
631bool startsWith(const S& str, const S& prefix)
632 /// Tests whether the string starts with the given prefix.
633{
634 return str.size() >= prefix.size() && equal(prefix.begin(), prefix.end(), str.begin());
635}
636
637
638template <class S>
639bool endsWith(const S& str, const S& suffix)
640 /// Tests whether the string ends with the given suffix.
641{
642 return str.size() >= suffix.size() && equal(suffix.rbegin(), suffix.rend(), str.rbegin());
643}
644
645
646//
647// case-insensitive string equality
648//
649
650
651template <typename charT>
652struct i_char_traits : public std::char_traits<charT>
653{
654 inline static bool eq(charT c1, charT c2)
655 {
656 return Ascii::toLower(c1) == Ascii::toLower(c2);
657 }
658
659 inline static bool ne(charT c1, charT c2)
660 {
661 return !eq(c1, c2);
662 }
663
664 inline static bool lt(charT c1, charT c2)
665 {
666 return Ascii::toLower(c1) < Ascii::toLower(c2);
667 }
668
669 static int compare(const charT* s1, const charT* s2, std::size_t n)
670 {
671 for (int i = 0; i < n && s1 && s2; ++i, ++s1, ++s2)
672 {
673 if (Ascii::toLower(*s1) == Ascii::toLower(*s2)) continue;
674 else if (Ascii::toLower(*s1) < Ascii::toLower(*s2)) return -1;
675 else return 1;
676 }
677
678 return 0;
679 }
680
681 static const charT* find(const charT* s, int n, charT a)
682 {
683 while(n-- > 0 && Ascii::toLower(*s) != Ascii::toLower(a)) { ++s; }
684 return s;
685 }
686};
687
688
689typedef std::basic_string<char, i_char_traits<char> > istring;
690 /// Case-insensitive std::string counterpart.
691
692
693template<typename T>
694std::size_t isubstr(const T& str, const T& sought)
695 /// Case-insensitive substring; searches for a substring
696 /// without regards to case.
697{
698 typename T::const_iterator it = std::search(str.begin(), str.end(),
699 sought.begin(), sought.end(),
700 i_char_traits<typename T::value_type>::eq);
701
702 if (it != str.end()) return it - str.begin();
703 else return static_cast<std::size_t>(T::npos);
704}
705
706
707struct CILess
708 /// Case-insensitive less-than functor; useful for standard maps
709 /// and sets with std::strings keys and case-insensitive ordering
710 /// requirement.
711{
712 inline bool operator() (const std::string& s1, const std::string& s2) const
713 {
714 return icompare(s1, s2) < 0;
715 }
716};
717
718
719} // namespace Poco
720
721
722#endif // Foundation_String_INCLUDED
723