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 | 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 | |
68 | template <class S> |
69 | S& 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 | |
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 | 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 | |
96 | template <class S> |
97 | S& 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 | |
113 | template <class S> |
114 | S 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 | |
127 | template <class S> |
128 | S& 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 | |
139 | template <class S> |
140 | S 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 | |
153 | template <class S> |
154 | S& 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 | |
168 | template <class S, class It> |
169 | int 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 | |
200 | template <class S> |
201 | int 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 | |
226 | template <class S> |
227 | int 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 | |
234 | template <class S> |
235 | int 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 | |
242 | template <class S> |
243 | int 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 | |
249 | template <class S> |
250 | int 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 | |
265 | template <class S> |
266 | int 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 | |
280 | template <class S> |
281 | int 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 | |
311 | template <class S> |
312 | int 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 | |
321 | template <class S> |
322 | int 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 | |
333 | 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); |
334 | int Foundation_API icompare(const std::string& str1, const std::string& str2); |
335 | int Foundation_API icompare(const std::string& str1, std::string::size_type n1, const std::string& str2, std::string::size_type n2); |
336 | int Foundation_API icompare(const std::string& str1, std::string::size_type n, const std::string& str2); |
337 | int Foundation_API icompare(const std::string& str1, std::string::size_type pos, std::string::size_type n, const std::string& str2); |
338 | 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); |
339 | 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); |
340 | int Foundation_API icompare(const std::string& str, std::string::size_type pos, std::string::size_type n, const std::string::value_type* ptr); |
341 | int Foundation_API icompare(const std::string& str, std::string::size_type pos, const std::string::value_type* ptr); |
342 | int Foundation_API icompare(const std::string& str, const std::string::value_type* ptr); |
343 | |
344 | |
345 | #endif |
346 | |
347 | |
348 | template <class S> |
349 | S 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 | |
378 | template <class S> |
379 | S 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 | |
387 | template <class S> |
388 | S& 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 | |
399 | template <class S> |
400 | S 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 | |
412 | template <class S> |
413 | S& 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 | |
437 | template <class S> |
438 | S& 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 | |
463 | template <class S> |
464 | S& 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 | |
483 | template <class S> |
484 | S& 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 | |
490 | template <class S> |
491 | S 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 | |
501 | template <class S> |
502 | S 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 | |
510 | template <class S> |
511 | S 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 | |
519 | template <class S> |
520 | S 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 | |
531 | Foundation_API std::string replace(const std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0); |
532 | 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); |
533 | 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); |
534 | Foundation_API std::string remove(const std::string& str, const std::string::value_type ch, std::string::size_type start = 0); |
535 | Foundation_API std::string& replaceInPlace(std::string& str, const std::string& from, const std::string& to, std::string::size_type start = 0); |
536 | 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); |
537 | 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); |
538 | Foundation_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 | |
544 | template <class S> |
545 | S 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 | |
555 | template <class S> |
556 | S 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 | |
567 | template <class S> |
568 | S 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 | |
580 | template <class S> |
581 | S 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 | |
594 | template <class S> |
595 | S 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 | |
609 | template <class S, class It> |
610 | S 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 | |
624 | template <class S> |
625 | bool 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 | |
632 | template <class S> |
633 | bool 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 | |
645 | template <typename charT> |
646 | struct 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 | |
683 | typedef std::basic_string<char, i_char_traits<char> > istring; |
684 | /// Case-insensitive std::string counterpart. |
685 | |
686 | |
687 | template<typename T> |
688 | std::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 | |
701 | struct 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 | |