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 return str;
406}
407
408
409#if !defined(POCO_NO_TEMPLATE_ICOMPARE)
410
411
412template <class S>
413S& replaceInPlace(S& str, const S& from, const S& to, typename S::size_type start = 0)
414{
415 poco_assert (from.size() > 0);
416
417 S result;
418 typename S::size_type pos = 0;
419 result.append(str, 0, start);
420 do
421 {
422 pos = str.find(from, start);
423 if (pos != S::npos)
424 {
425 result.append(str, start, pos - start);
426 result.append(to);
427 start = pos + from.length();
428 }
429 else result.append(str, start, str.size() - start);
430 }
431 while (pos != S::npos);
432 str.swap(result);
433 return str;
434}
435
436
437template <class S>
438S& replaceInPlace(S& str, const typename S::value_type* from, const typename S::value_type* to, typename S::size_type start = 0)
439{
440 poco_assert (*from);
441
442 S result;
443 typename S::size_type pos = 0;
444 typename S::size_type fromLen = std::strlen(from);
445 result.append(str, 0, start);
446 do
447 {
448 pos = str.find(from, start);
449 if (pos != S::npos)
450 {
451 result.append(str, start, pos - start);
452 result.append(to);
453 start = pos + fromLen;
454 }
455 else result.append(str, start, str.size() - start);
456 }
457 while (pos != S::npos);
458 str.swap(result);
459 return str;
460}
461
462
463template <class S>
464S& replaceInPlace(S& str, const typename S::value_type from, const typename S::value_type to = 0, typename S::size_type start = 0)
465{
466 if (from == to) return str;
467
468 typename S::size_type pos = 0;
469 do
470 {
471 pos = str.find(from, start);
472 if (pos != S::npos)
473 {
474 if (to) str[pos] = to;
475 else str.erase(pos, 1);
476 }
477 } while (pos != S::npos);
478
479 return str;
480}
481
482
483template <class S>
484S& removeInPlace(S& str, const typename S::value_type ch, typename S::size_type start = 0)
485{
486 return replaceInPlace(str, ch, 0, start);
487}
488
489
490template <class S>
491S replace(const S& str, const S& from, const S& to, typename S::size_type start = 0)
492 /// Replace all occurrences of from (which must not be the empty string)
493 /// in str with to, starting at position start.
494{
495 S result(str);
496 replaceInPlace(result, from, to, start);
497 return result;
498}
499
500
501template <class S>
502S replace(const S& str, const typename S::value_type* from, const typename S::value_type* to, typename S::size_type start = 0)
503{
504 S result(str);
505 replaceInPlace(result, from, to, start);
506 return result;
507}
508
509
510template <class S>
511S replace(const S& str, const typename S::value_type from, const typename S::value_type to = 0, typename S::size_type start = 0)
512{
513 S result(str);
514 replaceInPlace(result, from, to, start);
515 return result;
516}
517
518
519template <class S>
520S remove(const S& str, const typename S::value_type ch, typename S::size_type start = 0)
521{
522 S result(str);
523 replaceInPlace(result, ch, 0, start);
524 return result;
525}
526
527
528#else
529
530
531Foundation_API std::string replace(const std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0);
532Foundation_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);
533Foundation_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);
534Foundation_API std::string remove(const std::string& str, const std::string::value_type ch, std::string::size_type start = 0);
535Foundation_API std::string& replaceInPlace(std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0);
536Foundation_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);
537Foundation_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);
538Foundation_API std::string& removeInPlace(std::string& str, const std::string::value_type ch, std::string::size_type start = 0);
539
540
541#endif
542
543
544template <class S>
545S cat(const S& s1, const S& s2)
546 /// Concatenates two strings.
547{
548 S result = s1;
549 result.reserve(s1.size() + s2.size());
550 result.append(s2);
551 return result;
552}
553
554
555template <class S>
556S cat(const S& s1, const S& s2, const S& s3)
557 /// Concatenates three strings.
558{
559 S result = s1;
560 result.reserve(s1.size() + s2.size() + s3.size());
561 result.append(s2);
562 result.append(s3);
563 return result;
564}
565
566
567template <class S>
568S cat(const S& s1, const S& s2, const S& s3, const S& s4)
569 /// Concatenates four strings.
570{
571 S result = s1;
572 result.reserve(s1.size() + s2.size() + s3.size() + s4.size());
573 result.append(s2);
574 result.append(s3);
575 result.append(s4);
576 return result;
577}
578
579
580template <class S>
581S cat(const S& s1, const S& s2, const S& s3, const S& s4, const S& s5)
582 /// Concatenates five strings.
583{
584 S result = s1;
585 result.reserve(s1.size() + s2.size() + s3.size() + s4.size() + s5.size());
586 result.append(s2);
587 result.append(s3);
588 result.append(s4);
589 result.append(s5);
590 return result;
591}
592
593
594template <class S>
595S cat(const S& s1, const S& s2, const S& s3, const S& s4, const S& s5, const S& s6)
596 /// Concatenates six strings.
597{
598 S result = s1;
599 result.reserve(s1.size() + s2.size() + s3.size() + s4.size() + s5.size() + s6.size());
600 result.append(s2);
601 result.append(s3);
602 result.append(s4);
603 result.append(s5);
604 result.append(s6);
605 return result;
606}
607
608
609template <class S, class It>
610S cat(const S& delim, const It& begin, const It& end)
611 /// Concatenates a sequence of strings, delimited
612 /// by the string given in delim.
613{
614 S result;
615 for (It it = begin; it != end; ++it)
616 {
617 if (!result.empty()) result.append(delim);
618 result += *it;
619 }
620 return result;
621}
622
623
624template <class S>
625bool startsWith(const S& str, const S& prefix)
626 /// Tests whether the string starts with the given prefix.
627{
628 return str.size() >= prefix.size() && equal(prefix.begin(), prefix.end(), str.begin());
629}
630
631
632template <class S>
633bool endsWith(const S& str, const S& suffix)
634 /// Tests whether the string ends with the given suffix.
635{
636 return str.size() >= suffix.size() && equal(suffix.rbegin(), suffix.rend(), str.rbegin());
637}
638
639
640//
641// case-insensitive string equality
642//
643
644
645template <typename charT>
646struct i_char_traits : public std::char_traits<charT>
647{
648 inline static bool eq(charT c1, charT c2)
649 {
650 return Ascii::toLower(c1) == Ascii::toLower(c2);
651 }
652
653 inline static bool ne(charT c1, charT c2)
654 {
655 return !eq(c1, c2);
656 }
657
658 inline static bool lt(charT c1, charT c2)
659 {
660 return Ascii::toLower(c1) < Ascii::toLower(c2);
661 }
662
663 static int compare(const charT* s1, const charT* s2, size_t n)
664 {
665 for (int i = 0; i < n && s1 && s2; ++i, ++s1, ++s2)
666 {
667 if (Ascii::toLower(*s1) == Ascii::toLower(*s2)) continue;
668 else if (Ascii::toLower(*s1) < Ascii::toLower(*s2)) return -1;
669 else return 1;
670 }
671
672 return 0;
673 }
674
675 static const charT* find(const charT* s, int n, charT a)
676 {
677 while(n-- > 0 && Ascii::toLower(*s) != Ascii::toLower(a)) { ++s; }
678 return s;
679 }
680};
681
682
683typedef std::basic_string<char, i_char_traits<char> > istring;
684 /// Case-insensitive std::string counterpart.
685
686
687template<typename T>
688std::size_t isubstr(const T& str, const T& sought)
689 /// Case-insensitive substring; searches for a substring
690 /// without regards to case.
691{
692 typename T::const_iterator it = std::search(str.begin(), str.end(),
693 sought.begin(), sought.end(),
694 i_char_traits<typename T::value_type>::eq);
695
696 if (it != str.end()) return it - str.begin();
697 else return static_cast<std::size_t>(T::npos);
698}
699
700
701struct CILess
702 /// Case-insensitive less-than functor; useful for standard maps
703 /// and sets with std::strings keys and case-insensitive ordering
704 /// requirement.
705{
706 inline bool operator() (const std::string& s1, const std::string& s2) const
707 {
708 return icompare(s1, s2) < 0;
709 }
710};
711
712
713} // namespace Poco
714
715
716#endif // Foundation_String_INCLUDED
717