Coverage Report

Created: 2023-10-14 06:19

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