Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
String.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Characters_String_h_
5#define _Stroika_Foundation_Characters_String_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <compare>
10#include <iosfwd>
11#include <locale>
12#include <string>
13#include <string_view>
14
16#include "Stroika/Foundation/Characters/SDKString.h"
19#include "Stroika/Foundation/Containers/Sequence.h"
20#include "Stroika/Foundation/Containers/Set.h"
24
25/**
26 * \file
27 *
28 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
29 *
30 * TODO:
31 *
32 * @todo Cleanup SubString (), and String::SubString_ use of SharedByValue<TRAITS>::ReadOnlyReference for
33 * performance. At some level - in String::SubString_ - we have a (hidden) sharedPtr and it would
34 * be safe and performant in that case to re-use that shared_ptr to make a new String envelope.
35 *
36 * However, I'm not sure its safe in general to have SharedByValue<TRAITS>::ReadOnlyReference expose
37 * its shared_ptr, which appears needed to make this happen.
38 *
39 * Not a biggie opportunity, so we can delay this -- LGP 2014-04-10
40 *
41 * @todo Add PadLeft/PadRight or FillLeft/FilLRight() - not sure which name is better. But idea is to
42 * produce a string which is identical to the orig except that IF start len < n, then expand it with
43 * the given arg char repeated on the left or right.
44 *
45 * @todo RFind() API should be embellished to include startAt etc, like regular Find () - but not 100%
46 * sure - think through...
47 *
48 * @todo MAYBE also add ReplaceOne() function (we have ReplaceAll() now) ; see Replace() API in this function? - maybe overload?
49 *
50 * @todo Move DOCS in the top of this file down to the appropriate major classes - and then review the implementation and make sure
51 * it is all correct for each (especially SetStorage () stuff looks questionable)
52 *
53 */
54
56 template <typename T>
57 class Set;
58}
59
61
62 /*
63 * \brief On Windows, affects the behavior of String::As<filesystem::path> ()
64 *
65 * On windows, its helpful when mapping String to std::filesystem::pathname to map certain common name
66 * prefixes to things that will be found on Windows.
67 *
68 * MSYS creates paths like /c/folder for c:/folder
69 * CYGWIN creates paths like /cygdrive/c/folder for c:/folder
70 *
71 * Automatically map these (since Stroika v3.0d6) in (was ToPath) As<filesystem::path> ();
72 *
73 * \see https://www.msys2.org/docs/filesystem-paths/
74 * this API is for getting strings from the commandline, or user input, or configuration files etc, where Cygwin
75 * or msys style paths maybe present. APIs that talk directly to the OS are more likely to more directly produce
76 * filesystem::path than String. Anyhow - because of this, on windows, its probably more helpful than not to map
77 * the MSYS/cygdrive crap to a path more likely to actually work right. --LGP 2024-03-06
78 */
79#ifndef qStroika_Foundation_Characters_AsPathAutoMapMSYSAndCygwin
80#define qStroika_Foundation_Characters_AsPathAutoMapMSYSAndCygwin qStroika_Platform_Windows
81#endif
82
83}
84
85#if qStroika_Foundation_Characters_AsPathAutoMapMSYSAndCygwin
86namespace std::filesystem {
87 class path; // forward declare for template specialization
88}
89#endif
90
92
93 class RegularExpression;
94 class RegularExpressionMatch;
95
96 /**
97 * Flag principally for LimitLength, but used elsewhere as well (e.g. ToString ()).
98 */
100 ePreferKeepLeft,
101 ePreferKeepRight,
102 ePreferKeepMid,
103
104 eDEFAULT = ePreferKeepLeft,
105
106 Stroika_Define_Enum_Bounds (ePreferKeepLeft, ePreferKeepMid)
107 };
108 using StringShorteningPreference::ePreferKeepLeft;
109 using StringShorteningPreference::ePreferKeepMid;
110 using StringShorteningPreference::ePreferKeepRight;
111
112 /**
113 * \brief returns true iff T == u8string, u16string, u32string, or wstring - which std::string types can be unambiguously converted to UNICODE
114 */
115 template <typename T>
116 concept IBasicUNICODEStdString = same_as<T, u8string> or same_as<T, u16string> or same_as<T, u32string> or same_as<T, wstring>;
117
118 /**
119 * \brief anything with a 'special .STRINGTYPE conversion' method to UNICODE string, such as filesystem::path
120 *
121 * Really, this is a thinly veiled attempt to avoid #include <filesystem> for modularity reasons.
122 */
123 template <typename T>
124 concept IStdPathLike2UNICODEString = requires (T t) {
125 { t.wstring () } -> same_as<wstring>;
126 } or requires (T t) {
127 { t.u8string () } -> same_as<u8string>;
128 } or requires (T t) {
129 { t.u16string () } -> same_as<u16string>;
130 } or requires (T t) {
131 { t.u32string () } -> same_as<u32string>;
132 };
133
134 class String;
135
136 /**
137 * The concept IConvertibleToString is satisfied iff the argument type can be used to construct a (Stroika) String.
138 * Note subtly, const char* is treated (as of Stroika v3) as convertible to String, but the characters must be ASCII, or
139 * an exception will be generated in the constructor.
140 */
141 template <typename T>
142 concept IConvertibleToString = convertible_to<T, String>;
143
144 /**
145 * \brief String is like std::u32string, except it is much easier to use, often much more space efficient, and more easily interoperates with other string types
146 *
147 * The Stroika String class is conceptually a sequence of (UNICODE) Characters, and so there is
148 * no obvious way to map the Stroika String to a std::string (in general). However, if you specify a codepage
149 * for conversion, or are converting to/from SDKString/SDKChar, or u8string, etc, there is builtin support for that.
150 *
151 * EOS Handling:
152 * The Stroika String class supports having embedded NUL-characters. It also supports
153 * easy construction from NUL-terminated character strings.
154 *
155 * Since Stroika v3, there is no longer c_str () support, since Stroika doesn't internally
156 * require NUL-terminated strings, and actively encourages different compact representations of
157 * strings (c_str() requires a choice of a particular encoding to make sense).
158 *
159 * About spans, and the \0 NUL-termination - generally do NOT include
160 * the NUL-character in your span! Stroika strings will allow this, and treat
161 * it as just another character, but its probably not what you meant.
162 *
163 * \note Narrow String handling
164 * Because the character set of strings of type 'char' is ambiguous, if you construct a String
165 * with char (char* etc) - it is somehow 'required' that the characters be ASCII. If using the FromConstantString () API
166 * , or operator""_k, it is checked with Require () - so assertion failure. If you construct
167 * with String::CTOR, it will generate a runtime exception (so more costly runtime checking).
168 *
169 * \note Satisfies Concepts:
170 * o regular<String>
171 *
172 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
173 *
174 * \note Design note - mutability vs. immutability
175 * https://github.com/SophistSolutions/Stroika/issues/1100 (STK-968) (see about deleting deprecated APIs and remnants of mutability) and c_str()
176 *
177 * String objects are IMMUTABLE (except for the OBVIOUS meaning case of operator= being allowed).
178 *
179 * String reps are IMMUTABLE.
180 *
181 * Use StringBuilder for a 'mutable' String (can be used mostly interchangeably with String).
182 *
183 * Current Mutating methods (as of v3.0d1x)
184 * o c_str () -- non-const deprecated in v3.0d13
185 * o SetCharAt - deprecated v3.0d12
186 * o c_str() (consider deprecating?)
187 * o operator= - deprecated v3.0d12
188 * o clear()- deprecated v3.0d12
189 * o Append - deprecated v3.0d12
190 * o operator+= - deprecated v3.0d12
191 * o erase() - deprecated v3.0d12
192 *
193 * SOMEWHAT ironically, the only of these methods hard to replace is the non-const c_str () - and maybe there
194 * not bad cuz I deprecated? COULD just deprecate ALL of these, and then the class is fully immutable. Probably
195 * easier to understand/reason about.
196 *
197 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
198 * o static_assert (totally_ordered<String>);
199 * o String::EqualsComparer, String::ThreeWayComparer and String::LessComparer provided with construction parameters to allow case insensitive compares
200 */
201 class String : public Traversal::Iterable<Character> {
202 private:
203 using inherited = Iterable<Character>;
204
205 protected:
206 class _IRep;
207
208 public:
209 /**
210 * All the constructors are obvious, except
211 * o NUL-character ARE allowed in strings, except for the case of single
212 * charX* argument constructors - which find the length based on
213 * the terminating NUL-character.
214 *
215 * o CTOR (PATHLIKE_TOSTRINGABLE&& s) - IStdPathLike2UNICODEString PATHLIKE_TOSTRINGABLE
216 * carefully excludes conflicting CTOR overloads, and purpose is to allow constructing a String
217 * from anything with a 'special conversion' method to UNICODE string, such as filesystem::path.
218 *
219 * \note Before Stroika v3.0d25, the basic_string_view<CHAR_T> constructors REQUIRED the characters viewed to last for the
220 * life of the application: they shared them, never copied. Now they check whether they can safely share them - a
221 * string literal's, in the program's read-only image (@see Execution::IsAddressReadOnlyAppLifetime) - and otherwise
222 * copy. To skip that check, for a literal, use "..."_k (or FromStringConstant).
223 *
224 * \note 'char' (using ASCII = char) constructors:
225 * Because the character-set of strings of type 'char' is ambiguous, their characters must be ASCII. That is
226 * checked at run time, and throws (as String::FromASCII did in Stroika v2.1) - the characters could come from
227 * anywhere: a user, a file, the network.
228 *
229 * The only exceptions, checked by assertion instead (Debug builds), are constants built into the program - code,
230 * not data - kept cheap: FromStringConstant, "..."_k, and a basic_string_view of a string literal (one in the
231 * program's read-only image; any other view is copied, and checked). By design there are no others: code that
232 * wants a constant cheaply has those three.
233 *
234 * \note the basic_string move Constructors MAY move or copy the underlying std string, but they still maintain
235 * the same requirements on their arguments as the copy basic_string constructors (eg. char must be ascii)
236 *
237 * \see also - FromUTF8, FromSDKString, FromNarrowSDKString, FromStringConstant, FromLatin1, which are all like constructors
238 * but with special names to avoid confusion and make clear their arguments, and not participate in overloading. Note, chose
239 * this path instead of FLAG argument and explicit on CTOR, cuz more terse.
240 */
241 String ();
242 explicit String (Character c);
243 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
244 String (const CHAR_T* cString);
245 template <Memory::ISpan SPAN_OF_CHAR_T>
248 template <IStdBasicStringCompatibleCharacter CHAR_T>
250 template <IStdBasicStringCompatibleCharacter CHAR_T>
252 template <IStdBasicStringCompatibleCharacter CHAR_T>
253 explicit String (basic_string<CHAR_T>&& s);
254 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
256 requires (not Memory::ISpan<CHAR_T>);
257 template <IStdPathLike2UNICODEString PATHLIKE_TOSTRINGABLE>
258 explicit String (PATHLIKE_TOSTRINGABLE&& s);
259 String (String&& from) noexcept = default;
260 String (const String& from) noexcept = default;
261
262 private:
263 template <IStdPathLike2UNICODEString PATHLIKE_TOSTRINGABLE>
264 static String mkSTR_ (PATHLIKE_TOSTRINGABLE&& s);
265
266 private:
267 static shared_ptr<_IRep> CTORFromBasicStringView_ (const basic_string_view<ASCII>& str);
268 static shared_ptr<_IRep> CTORFromBasicStringView_ (const basic_string_view<char8_t>& str);
269 static shared_ptr<_IRep> CTORFromBasicStringView_ (const basic_string_view<char16_t>& str);
270 static shared_ptr<_IRep> CTORFromBasicStringView_ (const basic_string_view<char32_t>& str);
271 static shared_ptr<_IRep> CTORFromBasicStringView_ (const basic_string_view<wchar_t>& str);
272
273 public:
274 ~String () = default;
275
276 protected:
277 /**
278 */
279 using _SafeReadRepAccessor = Iterable<Character>::_SafeReadRepAccessor<_IRep>;
280
281 protected:
282 /**
283 * \pre rep MUST be not-null
284 * However, with move constructor, it maybe null on exit.
285 */
286 String (const shared_ptr<_IRep>& rep) noexcept;
287 String (shared_ptr<_IRep>&& rep) noexcept;
288
289 public:
290 nonvirtual String& operator= (String&& rhs) noexcept = default;
291 nonvirtual String& operator= (const String& rhs) noexcept = default;
292
293 public:
294 /**
295 * Create a String object from a 'char-based' utf-8 encoded string.
296 *
297 * \par Example Usage
298 * \code
299 * EXPECT_TRUE (string{u8"שלום"} == String::FromUTF8 (u8"שלום").AsUTF8 ());
300 * \endcode
301 *
302 * \note This is not generally needed, as you can just use the String::CTOR, but for cases like
303 * std::string-> String - where the conversion needs extra information (an assertion about character encoding of source characters).
304 *
305 * \note Reading improperly encoded text may result in a RuntimeException indicating improperly encoded characters.
306 */
307 template <typename CHAR_T>
310 template <typename CHAR_T>
313 template <typename CHAR_T>
314 static String FromUTF8 (const CHAR_T* from)
316
317 public:
318 /**
319 * Create a String object from a 'SDKChar' (os-setting - current code page) encoded string.
320 * See @SDKChar
321 * See @SDKString
322 *
323 * \note Reading improperly encoded text may result in a RuntimeException indicating improperly encoded characters.
324 *
325 * \note for span<> overload - consider using AdjustNulTerminatedStringSpan
326 */
327 static String FromSDKString (const SDKChar* from);
329 static String FromSDKString (const SDKString& from);
330
331 public:
332 /**
333 * Create a String object from a 'char-based' (os-setting - current code page) encoded string.
334 *
335 * \note Reading improperly encoded text may result in a RuntimeException indicating improperly encoded characters.
336 *
337 * \note for span<> overload - consider using AdjustNulTerminatedStringSpan
338 */
339 static String FromNarrowSDKString (const char* from);
341 static String FromNarrowSDKString (const string& from);
342
343 public:
344 /**
345 * Create a String object from a char based on the encoding from the argument locale.
346 * This throws an exception if there is an error performing the conversion.
347 *
348 * \note Reading improperly encoded text may result in a RuntimeException indicating improperly encoded characters.
349 *
350 * \note for span<> overload - consider using AdjustNulTerminatedStringSpan
351 */
352 static String FromNarrowString (const char* from, const locale& l);
354 static String FromNarrowString (const string& from, const locale& l);
355
356 public:
357 /**
358 * \brief Take the given argument data (constant span) - which must remain unchanged - constant - for the application lifetime - and treat it as a Stroika String object
359 *
360 * This allows creation of String objects with fewer memory allocations and less copyinh, and more efficient storage, in most situations
361 *
362 * The resulting String is a perfectly compliant Stroika String (somewhat akin to std::string_view vs std::string).
363 *
364 * \par Example:
365 * \code
366 * String tmp1 = "FRED";
367 * String tmp2 = String{"FRED"};
368 * String tmp3 = String::FromStringConstant ("FRED"); // same as 2 above, but faster
369 * String tmp4 = "FRED"sv; // shares too, having checked it is a literal
370 * String tmp5 = "FRED"_k; // equivalent to FromStringConstant
371 * \endcode
372 *
373 * \em WARNING - BE VERY CAREFUL - be sure arguments have application lifetime (intended use case is C string literals).
374 *
375 * \note 'application lifetime': the characters must stay unchanged, at that address, for as long as anything may still
376 * use them - and as copies of a String share them (copy-on-write), and can be kept anywhere, in practice that means
377 * until the program ends: a string literal. Break the rule and the behavior is undefined - it may well appear to
378 * work, but nothing promises it.
379 *
380 * \pre argument string MAY contain embedded nul characters (but for char* overloads wrong size inferred).
381 *
382 * \note In Stroika v2.1 this was called class String_ExternalMemoryOwnership_ApplicationLifetime.
383 * \note In Stroika v2.1 this was called class String_Constant.
384 * \note In Stroika v2.1 this required NUL-char termination, but no longer
385 *
386 * \note FromStringConstant with 'char' - REQUIRES that the char elements are ASCII (someday this maybe lifted and interpret as Latin1),
387 * checked by assertion only (Debug builds), as its argument is a constant built into the program (@see the 'char'
388 * note on the constructors). For the case of char, we also do not check/require the nul-termination bit.
389 */
390 template <size_t SIZE, IUNICODECanUnambiguouslyConvertFrom CHAR_T>
391 static String FromStringConstant (const CHAR_T (&cString)[SIZE]);
392 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
394 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
396 static String FromStringConstant (span<const ASCII> s); // better impl in CPP file
397 static String FromStringConstant (span<const char16_t> s); // ""
398 static String FromStringConstant (span<const wchar_t> s); // "" (inl file)
399 static String FromStringConstant (span<const char32_t> s); // ""
400
401 public:
402 /**
403 * Create a String object from UNICODE Latin-1 Supplement (https://en.wikipedia.org/wiki/Latin-1_Supplement)
404 *
405 * This is roughly, but not exactly, the same as the ISO-Latin-1 single-byte character set (https://en.wikipedia.org/wiki/ISO/IEC_8859-1)
406 *
407 * \note if character code point >= 256, this will throw an exception - not defined for that range (only checked if sizeof (CHAR_T) > 1)
408 *
409 * @aliases From8bitASCII () or FromExtendedASCII ()
410 *
411 * \note for span<> overload - consider using AdjustNulTerminatedStringSpan
412 */
413 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
414 static String FromLatin1 (const CHAR_T* cString);
415 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
417 template <IStdBasicStringCompatibleCharacter CHAR_T>
418 static String FromLatin1 (const basic_string<CHAR_T>& s);
419
420 public:
421 /**
422 * \brief appends 'rhs' string to this string (without modifying this string) and returns the combined string
423 *
424 * @see Append() for a similar function that modifies 'this'
425 */
426 template <typename T>
427 nonvirtual String Concatenate (T&& rhs) const
429
430 private:
431 nonvirtual String Concatenate_ (const String& rhs) const;
432
433 public:
434 /**
435 * Returns the number of characters in the String. Note that this may not be the same as bytes,
436 * does not include NUL termination, and doesn't in any way respect NUL termination (meaning
437 * a nul-character is allowed in a Stroika string.
438 *
439 * @aliases GetLength ()
440 */
441 nonvirtual size_t size () const noexcept;
442
443 public:
444 /**
445 */
446 nonvirtual bool empty () const noexcept;
447
448 public:
449 /**
450 */
451 nonvirtual const Character GetCharAt (size_t i) const noexcept;
452
453 public:
454 /**
455 * \brief return (read-only) Character object
456 *
457 * @aliases GetCharAt (size_t i) const;
458 *
459 * \note returns const due to https://github.com/SophistSolutions/Stroika/issues/510 (STK-376) - so cannot accidentally have illusion of assignment being legal
460 */
461 nonvirtual const Character operator[] (size_t i) const noexcept;
462
463 public:
464 /**
465 * InsertAt() constructs a new string by taking this string, and inserting the argument
466 * characters.
467 *
468 * \em Note that for repeated insertions, this is much less efficient than just
469 * using StringBuilder.
470 *
471 * \note that if at == this->size (), you are appending.
472 *
473 * \note for span<> overload - consider using AdjustNulTerminatedStringSpan
474 */
475 nonvirtual String InsertAt (Character c, size_t at) const;
476 nonvirtual String InsertAt (const String& s, size_t at) const;
477 nonvirtual String InsertAt (span<const Character> s, size_t at) const;
478 nonvirtual String InsertAt (span<Character> s, size_t at) const;
479
480 public:
481 /**
482 * Remove the characters at 'charAt' (RemoveAt/1) or between 'from' and 'to' (const method - doesn't modify this)
483 *
484 * It is an error if this implies removing characters off the end of the string.
485 *
486 * \par Example Usage
487 * \code
488 * String mungedData = "04 July 2014";
489 * if (optional<pair<size_t, size_t>> i = mungedData.Find (RegularExpression{"0[^\b]"})) {
490 * mungedData = mungedData.RemoveAt (*i);
491 * }
492 * \endcode
493 *
494 * \pre (charAt < size ())
495 * \pre (from <= to)
496 * \pre (to <= size ())
497 *
498 * \em Note that this is quite inefficient: consider using StringBuilder
499 */
500 nonvirtual String RemoveAt (size_t charAt) const;
501 nonvirtual String RemoveAt (size_t from, size_t to) const;
502 nonvirtual String RemoveAt (pair<size_t, size_t> fromTo) const;
503
504 public:
505 /**
506 * Remove the first occurrence of Character 'c'/'/subString/ from the string. Not an error if none
507 * found. Doesn't modify this (const method) - returns resulting string.
508 *
509 * \em Note that this is quite inefficient: consider using StringBuffer
510 */
511 nonvirtual String RemoveFirstIf (Character c) const;
513
514 public:
515 /**
516 * Remove the all occurrences of Character 'c/subString' from this string
517 * (walking front to back - if removal creates one, it too is removed).
518 * Not an error if none found. Doesn't modify this (const method) - returns resulting string.
519 */
520 nonvirtual String RemoveAll (Character c) const;
522
523 public:
524 /**
525 * OVERLOADS WITH size_t:
526 *
527 * Produce a substring of this string, starting at 'from', and up to 'to' (or end of string
528 * for one-arg overload).
529 *
530 * *NB* This function treats the second argument differently than String::substr () -
531 * which respects the STL basic_string API. This function treats the second argument
532 * as a 'to', STL substr() treats it as a count. This amounts to the same thing for the
533 * very common cases of substr(N) - because second argument is defaulted, and,
534 * substr (0, N) - because then the count and end are the same.
535 *
536 * \pre (from <= to);
537 * \pre (to <= size ()); // for 2-arg variant
538 *
539 * \par Example Usage
540 * \code
541 * String tmp { "This is good" };
542 * Assert (tmp.SubString (5) == "is good");
543 * \endcode
544 *
545 * \par Example Usage
546 * \code
547 * const String kTest_ { "a=b"sv };
548 * const String kLbl2LookFor_ { "a="_k };
549 * if (resultLine.Find (kLbl2LookFor_)) {
550 * String tmp { resultLine.SubString (kLbl2LookFor_.length ()) };
551 * }
552 * Assert (tmp == "b");
553 * \endcode
554 *
555 * OVERLOADS WITH ptrdiff_t:
556 *
557 * This is like SubString() except that if from/to are negative, they are treated as relative to the end
558 * of the String.
559 *
560 * So for example, SubString (0, -1) is equivalent to SubString (0, size () - 1) - and so is an
561 * error if the string is empty.
562 *
563 * Similarly, SubString (-5) is equivalent to SubString (size ()-5, size ()) - so can be used
564 * to grab the end of a string.
565 *
566 * \pre (adjustedFrom <= adjustedTo);
567 * \pre (adjustedTo <= size ()); // for 2-arg variant
568 *
569 * \note \em Design Note
570 * We chose not to overload SubString() with this functionality because it would have been to easy
571 * to mask bugs.
572 *
573 * \note \em Design Note
574 * This was originally inspired by Python arrays. From https://docs.python.org/2/tutorial/introduction.html:
575 * Indices may also be negative numbers, to start counting from the right
576 *
577 * @aliases
578 * This API is identical to the javascript String.slice () method/behavior
579 * @see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
580 * 15.5.4.13 String.prototype.slice (start, end)
581 *
582 * @aliases
583 * This API - when called with negative indexes - used to be called CircularSubString ().
584 *
585 * @see substr
586 * @see SafeSubString
587 */
591 nonvirtual String SubString (SZ1 from, SZ2 to) const;
592
593 public:
594 /**
595 * Like SubString(), but no requirements on from/to. These are just adjusted to the edge of the string
596 * if the exceed those endpoints. And if arguments are <0, they are interpreted as end-relative.
597 *
598 * @aliases
599 * This API - when called with negative indexes - used to be called SafeCircularSubString ().
600 *
601 * @see substr
602 * @see SubString
603 */
607 nonvirtual String SafeSubString (SZ1 from, SZ2 to) const;
608
609 public:
610 /**
611 * \brief Return a substring of this string, starting at 'argument' n. If n > size(), return empty string.
612 *
613 * \note This hides inherited Skip, and returns a subtype of what it returns, and is just slightly more
614 * efficient, but otherwise completley compatible behavior.
615 */
616 nonvirtual String Skip (size_t n) const;
617
618 public:
619 /**
620 * Return 'count' copies of this String (concatenated after one another).
621 */
622 nonvirtual String Repeat (unsigned int count) const;
623
624 public:
625 /**
626 * Returns true if the argument character or string is found anywhere inside this string.
627 * This is equivalent to
628 * return Matches (".*" + X + ".*"); // If X had no characters which look like they are part of
629 * // a regular expression
630 *
631 * @see Match
632 */
633 nonvirtual bool Contains (Character c, CompareOptions co = eWithCase) const;
634 nonvirtual bool Contains (const String& subString, CompareOptions co = eWithCase) const;
635
636 public:
637 /**
638 *
639 */
640 nonvirtual bool ContainsAny (Iterable<Character> cs, CompareOptions co = eWithCase) const;
641
642 public:
643 /**
644 * Returns true iff the given substring is contained in this string.
645 *
646 * Similar to:
647 * return Matches (X + ".*");
648 * except for the fact that with StartsWith() doesn't interpret 'X' as a regular expression
649 *
650 * \pre not subString.empty () -- for the subString overload (because otherwise "".StartsWith("") would be ill-defined)
651 *
652 * @see Match
653 * @see EndsWith
654 */
655 nonvirtual bool StartsWith (const Character& c, CompareOptions co = eWithCase) const;
656 nonvirtual bool StartsWith (const String& subString, CompareOptions co = eWithCase) const;
657
658 public:
659 /**
660 * Returns true iff the given substring is contained in this string.
661 *
662 * Similar to:
663 * return Matches (X + ".*");
664 * except for the fact that with StartsWith() doesn't interpret 'X' as a regular expression
665 *
666 * \pre not subString.empty () -- for the subString overload (because otherwise "".EndsWith("") would be ill-defined)
667 *
668 * @see Match
669 * @see StartsWith
670 */
671 nonvirtual bool EndsWith (const Character& c, CompareOptions co = eWithCase) const;
672 nonvirtual bool EndsWith (const String& subString, CompareOptions co = eWithCase) const;
673
674 public:
675 /**
676 * \brief Return *this if it ends with argument character, or append 'c' so that it ends with a 'c'.
677 *
678 * \note this is to specific-purpose to be a very sensible API, but I find it pretty often pretty useful. So what-the-heck.
679 */
680 nonvirtual String AssureEndsWith (const Character& c, CompareOptions co = eWithCase) const;
681
682 public:
683 /**
684 * Apply the given regular expression return true if it matches this string. This only
685 * returns true if the expression matches the ENTIRE string - all the way to the end.
686 * @see FindEach() or @see Find - to find a set of things which match.
687 *
688 * \par Example Usage
689 * \code
690 * Assert (String{"abc"}.Matches ("abc"));
691 * Assert (not (String{"abc"}.Matches ("bc")));
692 * Assert (String{"abc"}.Matches (".*bc"));
693 * Assert (not String{"abc"}.Matches ("b.*c"));
694 * \endcode
695 *
696 * \par Example Usage
697 * \code
698 * static const RegularExpression kSonosRE_{"([0-9.:]*)( - .*)"_RegEx};
699 * static const String kTestStr_{"192.168.244.104 - Sonos Play:5"};
700 * optional<String> match1;
701 * optional<String> match2;
702 * EXPECT_TRUE (kTestStr_.Matches (kSonosRE_, &match1, &match2) and match1 == "192.168.244.104" and match2 == " - Sonos Play:5");
703 * EXPECT_EQ (kTestStr_.Matches<1> (kSonosRE_), make_tuple ("192.168.244.104"_k));
704 * EXPECT_EQ (kTestStr_.Matches<2> (kSonosRE_), make_tuple ("192.168.244.104"_k, "Sonos Play:5"_k));
705 * \endcode
706 *
707 * \par Example Usage
708 * \code
709 * // https://tools.ietf.org/html/rfc3986#appendix-B
710 * static const RegularExpression kParseURLRegExp_{"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"_RegEx};
711 * optional<String> scheme;
712 * optional<String> authority;
713 * optional<String> path;
714 * optional<String> query;
715 * optional<String> fragment;
716 * if (rawURL.Matches (kParseURLRegExp_, nullptr, &scheme, nullptr, &authority, &path, nullptr, &query, nullptr, &fragment)) {
717 * DbgTrace ("***good - scheme={}"_f, scheme);
718 * DbgTrace ("***good - authority={}"_f, authority);
719 * DbgTrace ("***good - path={}"_f, path);
720 * DbgTrace ("***good - query={}"_f, query);
721 * DbgTrace ("***good - fragment={}"_f, fragment);
722 * }
723 * \endcode
724 *
725 * \par Example Usage
726 * \code
727 * // Praat 6.4.23 (October 27 2024)
728 * String processRunnerOutput = "Praat 6.4.23 (October 27 2024)";
729 * String version;
730 * return processRunnerOutput.Matches ("(\\w+)\\s([\\w\\.]+).*"_RegEx, nullptr, &version)? version: "???"sv;
731 * return get<1> (processRunnerOutput.Matches<1> ("\\w+\\s([\\w\\.]+).*"_RegEx).value_or(make_tuple("???"_k))); // Or better
732 * \endcode
733 *
734 * Details on the regular expression language/format can be found at:
735 * http://en.wikipedia.org/wiki/C%2B%2B11#Regular_expressions
736 *
737 * \note If any 'sub-match' arguments are passed to Match, they MUST be of type optional<String>* or nullptr.
738 * Passing nullptr allows matched parameters to not be returned, but still identified positionally (by index).
739 *
740 * @see Contains
741 * @see StartsWith
742 * @see EndsWith
743 * @see Find
744 * @see FindEach
745 */
746 nonvirtual bool Matches (const RegularExpression& regEx) const;
747 nonvirtual bool Matches (const RegularExpression& regEx, Containers::Sequence<String>* matches) const;
748 template <Common::IAnyOf<optional<String>*, String*, nullptr_t>... OPTIONAL_STRINGS>
750 template <size_t I>
751 nonvirtual optional<Common::RepeatedTuple_t<I, String>> Matches (const RegularExpression& regEx) const;
752
753 public:
754 /**
755 * Find returns the index of the first occurrence of the given Character/substring argument in
756 * this string. Find () always returns a valid string index, which is followed by the
757 * given substring, or nullopt otherwise.
758 *
759 * Find () can optionally be provided a 'startAt' offset to begin the search at.
760 *
761 * And the overload taking a RegularExpression - returns BOTH the location where the match
762 * is found, as well as the end of the match.
763 *
764 * Note - for the special case of Find(empty-string) - the return value is 0 if this string
765 * is non-empty, and nullopt if this string was empty.
766 *
767 * @aliases - could have been called IndexOf ()
768 *
769 * \pre (startAt <= size ());
770 *
771 * \par Example Usage
772 * \code
773 * const String kTest_{ "a=b" };
774 * const String kLbl2LookFor_ { "a=" };
775 * if (kTest_.Find (kLbl2LookFor_)) {
776 * String tmp { kTest_.SubString (kLbl2LookFor_.length ()) };
777 * }
778 * Assert (tmp == "b");
779 * \endcode
780 *
781 * @see FindEach ()
782 * @see FindEachString ()
783 * @see Tokenize
784 */
785 nonvirtual optional<size_t> Find (Character c, CompareOptions co = eWithCase) const;
786 nonvirtual optional<size_t> Find (Character c, size_t startAt, CompareOptions co = eWithCase) const;
787 nonvirtual optional<size_t> Find (const String& subString, CompareOptions co = eWithCase) const;
788 nonvirtual optional<size_t> Find (const String& subString, size_t startAt, CompareOptions co = eWithCase) const;
789 nonvirtual optional<pair<size_t, size_t>> Find (const RegularExpression& regEx, size_t startAt = 0) const;
790 nonvirtual Traversal::Iterator<Character> Find (const function<bool (Character item)>& that) const;
791
792 public:
793 /**
794 * This is just like Find, but captures all the matching results in an iterable result.
795 * The reason the overload for RegularExpression's returns a list of pair<size_t,size_t> is because
796 * the endpoint of the match is ambiguous. For fixed string Find, the end of match is computable
797 * from the arguments.
798 *
799 * FindEach () can be more handy to use than directly using Find () in scenarios where you want
800 * to iterate over each match:
801 * e.g.:
802 * for (auto i : s.FindEach ("xxx")) {....}
803 *
804 * Also, to count matches, you can use:
805 * size_t nMatches = FindEach (matchexp).size ();
806 *
807 * Note: FindEach handles the special case of an empty match as ignored, so FindEach(empty-str-or-regexp)
808 * always returns an empty list. Also - for the String case, it returns distinct matches, so if you
809 * search String{"AAAA"}.FindEach ("AA"), you will get 2 answers ({0, 2}).
810 *
811 * @see Find ()
812 * @see FindEachString ()
813 * @see Matches ()
814 */
815 nonvirtual Containers::Sequence<pair<size_t, size_t>> FindEach (const RegularExpression& regEx) const;
816 nonvirtual Containers::Sequence<size_t> FindEach (const String& string2SearchFor, CompareOptions co = eWithCase) const;
817
818 public:
819 /**
820 * \par Example Usage
821 * \code
822 * const String kTest_{ "a=b,"sv };
823 * const RegularExpression kRE_{ "a=(.*)[, ]" };
824 * Sequence<String> tmp1{ kTest_.FindEachString (kRE_) };
825 * Assert (tmp1.size () == 1 and tmp1[0] == "a=b,");
826 * Sequence<RegularExpressionMatch> tmp2 { kTest_.FindEachMatch (kRE_) };
827 * Assert (tmp2.size () == 1 and tmp2[0].GetFullMatch () == "a=b," and tmp2[0].GetSubMatches () == Sequence<String>{"b"});
828 * \endcode
829 *
830 * @see Find ()
831 * @see FindEachString ()
832 * @see Matches ()
833 */
834 nonvirtual Containers::Sequence<RegularExpressionMatch> FindEachMatch (const RegularExpression& regEx) const;
835
836 public:
837 /**
838 * \par Example Usage
839 * \code
840 * const String kTest_ { "a=b, c=d"_k };
841 * const RegularExpression kRE_ { "(.)=(.)" };
842 * Assert ((kTest_.FindEachString (kRE_) == vector<String>{"a=b", "c=d"}));
843 * \endcode
844 *
845 * @see Find ()
846 * @see FindEachMatch ()
847 * @see Matches ()
848 */
849 nonvirtual Containers::Sequence<String> FindEachString (const RegularExpression& regEx) const;
850
851 public:
852 /**
853 * RFind (substring) returns the index of the last occurrence of the given substring in
854 * this string. This function always returns a valid string index, which is followed by the
855 * given substring, or optional<size_t> {} otherwise.
856 *
857 * @aliases RIndexOf ()
858 */
859 nonvirtual optional<size_t> RFind (Character c) const noexcept;
860 nonvirtual optional<size_t> RFind (const String& subString) const;
861
862 public:
863 /**
864 * Replace the range of this string with the given replacement. Const method: just creates new string as described.
865 */
866 nonvirtual String Replace (size_t from, size_t to, const String& replacement) const;
867 nonvirtual String Replace (pair<size_t, size_t> fromTo, const String& replacement) const;
868
869 public:
870 /**
871 * Apply the given regular expression, with 'with' and replace each match. This doesn't
872 * modify this string, but returns the replacement string.
873 *
874 * CHECK - BUT HI HTINK WE DEFINE TO REPLACE ALL? OR MAKE PARAM?
875 * See regex_replace () for definition of the regEx language
876 *
877 * Require (not string2SearchFor.empty ());
878 *
879 * \par Example Usage
880 * \code
881 * mungedData = mungedData.ReplaceAll (RegularExpression{ "\\b0+" }, ""); // strip all leading zeros
882 * \endcode
883 *
884 * \par Example Usage
885 * \code
886 * String a = "a b \n\t c";
887 * EXPECT_EQ (a.ReplaceAll (RegularExpression{"\\s+"sv}, " "sv), "a b c");
888 * EXPECT_EQ (a.ReplaceAll ("\\s+"_RegEx, " "sv), "a b c");
889 * \endcode
890 *
891 * Note - it IS legal to have with contain the original search for string, or even
892 * to have it 'created' as part of where it gets
893 * inserted. The implementation will only replace those that pre-existed.
894 *
895 * \note To perform a regular expression replace-all, which is case insensitive, create the regular expression with CompareOptions::eCaseInsensitive
896 *
897 * \note ReplaceAll could have been called 'SafeString' or 'FilteredString' (was at one point - replaces that functionality)
898 */
900 nonvirtual String ReplaceAll (const String& string2SearchFor, const String& with, CompareOptions co = eWithCase) const;
901 nonvirtual String ReplaceAll (const function<bool (Character)>& replaceCharP, const String& with) const;
902 nonvirtual String ReplaceAll (const Containers::Set<Character>& charSet, const String& with) const;
903
904 public:
905 /**
906 * Replace any CR or LF or CRLF sequences with plain NL-terminated text.
907 */
908 nonvirtual String NormalizeTextToNL () const;
909
910 public:
911 /**
912 * \brief Replace sequences of whitespace characters (space, tab, newline etc) with a single space (or argument space character).
913 *
914 * \note see Qt 'QString::simplify()'.Idea is Trim () (right and left) - plus replace contiguous substrings with* Character::IsSpace () with a
915 * single (given) space character.
916 */
918
919 public:
920 /**
921 * Break this String into constituent parts. This is a simplistic API but at least handy as is.
922 *
923 * The caller can specify the token separators by set, by lambda. This defaults to the lambda "isWhitespace".
924 *
925 * This is often called 'Split' in other APIs. This is NOT (as is now) a replacement for flex, but just for
926 * simple, but common string splitting needs (though if I had a regexp param, it may approach the power of flex).
927 *
928 * \note If this->length () == 0, this method returns a list of length 0;
929 * \note Its fine for the split character/characters to be missing, in which case this
930 * returns a list of length 1
931 *
932 * \par Example Usage
933 * \code
934 * String t { "ABC DEF G" };
935 * Assert (t.Tokenize ().length () == 3);
936 * Assert (t.Tokenize ()[1] == "DEF");
937 * \endcode
938 *
939 * \par Example Usage
940 * \code
941 * String t { "foo= 7" };
942 * auto tt = t.Tokenize ({ '=' });
943 * Assert (t.length () == 2);
944 * Assert (t[1] == " 7");
945 * Assert (t[1].Trim () == "7");
946 * \endcode
947 *
948 * \par Example Usage
949 * \code
950 * String t { "foo= 7" };
951 * auto tt = t.Tokenize ({ '=', ' ' });
952 * Assert (t.length () == 2);
953 * Assert (t[1] == "7");
954 * \endcode
955 *
956 * @see Find
957 *
958 * TODO:
959 * @todo Review:
960 * http://qt-project.org/doc/qt-5.0/qtcore/qstring.html#split
961 * especially:
962 * QString line = "forename\tmiddlename surname \t \t phone";
963 * QRegularExpression sep("\\s+");
964 * str = line.section(sep, 2, 2); // str == "surname"
965 * str = line.section(sep, -3, -2); // str == "middlename surname"
966 * Make sure our Find/Tokenize is at least this simple, and maybe diff between find and split
967 * is FIND the regular expression names the things looked for and SPLIT() uses regexp to name the separators?
968 * Add something like the above to the String String demo app (when it exists)
969 */
970 nonvirtual Containers::Sequence<String> Tokenize () const;
971 nonvirtual Containers::Sequence<String> Tokenize (const function<bool (Character)>& isTokenSeperator) const;
972 nonvirtual Containers::Sequence<String> Tokenize (const RegularExpression& isSeparator) const;
973 nonvirtual Containers::Sequence<String> Tokenize (const Containers::Set<Character>& delimiters) const;
974
975 public:
976 /**
977 * \brief break the String into a series of lines;
978 *
979 * \note could almost be done with Tokenize(), except for the one-sided nl-specific trimming.
980 *
981 * \note removes line-endings (\r\n, or \n, or \r).
982 */
983 nonvirtual Containers::Sequence<String> AsLines () const;
984
985 public:
986 /**
987 * \brief Breaks this string into Lines, with AsLines (), and applies the argument filter (as if with .Map<>) producing a subset of the lines which match
988 *
989 * note this is useful to replace 'shell script' logic where you might run some command and grep through its output for all
990 * matching lines.
991 *
992 * \par Example Usage
993 * \code
994 * String firstALineOrEmpty = String{"...e.g. from output of ProcessRunner..."}.Grep ("a:").NthValue (0);
995 * \endcode
996 */
997 nonvirtual Containers::Sequence<String> Grep (const String& fgrepArg) const;
998 nonvirtual Containers::Sequence<String> Grep (const RegularExpression& egrepArg) const;
999
1000 public:
1001 /**
1002 * \brief Useful to replace 'awk print $3' - replace with Col(2) - zero based
1003 *
1004 * default separator = = "\\s+"_RegEx;
1005 *
1006 * \par Example Usage
1007 * \code
1008 * Assert (String{"ffmpeg version 7.1"}.Col (2) == "7.1");
1009 * \endcode
1010 *
1011 */
1012 nonvirtual optional<String> Col (size_t i) const;
1013 nonvirtual optional<String> Col (size_t i, const RegularExpression& separator) const;
1014
1015 public:
1016 /**
1017 * \brief see Col(i) - but with default value of empty string
1018 */
1019 nonvirtual String ColValue (size_t i, const String& valueIfMissing = {}) const;
1020
1021 public:
1022 /**
1023 * String LTrim () scans the characters form the left to right, and applies the given
1024 * 'shouldBeTrimmed' function (defaults to IsWhitespace). All such characters are removed,
1025 * and the resulting string is returned. This does not modify the current string its
1026 * applied to - just returns the trimmed string.
1027 */
1028 nonvirtual String LTrim (bool (*shouldBeTrimmed) (Character) = Character::IsWhitespace) const;
1029
1030 public:
1031 /**
1032 * String RTrim () scans the characters form the right to left, and applies the given
1033 * 'shouldBeTrimmed' function (defaults to IsWhitespace). All such characters are removed,
1034 * and the resulting string is returned. This does not modify the current string its
1035 * applied to - just returns the trimmed string.
1036 *
1037 * \par Example Usage
1038 * \code
1039 * String name = origName.RTrim ([] (Character c) { return c == '\\';}); // Trim a trailing backslash(s), if present
1040 * \endcode
1041 */
1042 nonvirtual String RTrim (bool (*shouldBeTrimmed) (Character) = Character::IsWhitespace) const;
1043
1044 public:
1045 /**
1046 * String Trim () is locally equivalent to RTrim (shouldBeTrimmed).LTrim (shouldBeTrimmed).
1047 */
1048 nonvirtual String Trim (bool (*shouldBeTrimmed) (Character) = Character::IsWhitespace) const;
1049
1050 public:
1051 /**
1052 * Walk the entire string, and produce a new string consisting of all characters for which
1053 * the predicate 'removeCharIf' returned false.
1054 */
1055 nonvirtual String StripAll (bool (*removeCharIf) (Character)) const;
1056
1057 public:
1058 /**
1059 * Combine the given array into a single string (typically comma space) separated.
1060 * If given a list of length n, this adds n-1 separators.
1061 *
1062 * \note .Net version - https://docs.microsoft.com/en-us/dotnet/api/system.string.join?redirectedfrom=MSDN&view=net-6.0#System_String_Join_System_String_System_String___
1063 * \note Java version - https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#join-java.lang.CharSequence-java.lang.CharSequence...-
1064 * \note Javascript - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
1065 *
1066 * \note - CONSIDER LOSING this as 'Iterable<>::Join' just appears to work better -- LGP 2025-01-21
1067 */
1068 static String Join (const Iterable<String>& list, const String& separator = ", "sv);
1069
1070 public:
1071 /**
1072 * Return a new string based on this string where each lower case character is replaced by its
1073 * upper case equivalent. Note that non-lower-case characters (such as punctuation) un unchanged.
1074 */
1075 nonvirtual String ToLowerCase () const;
1076
1077 public:
1078 /**
1079 * Return a new string based on this string where each lower case character is replaced by its
1080 * upper case equivalent. Note that non-upper-case characters (such as punctuation) un unchanged.
1081 */
1082 nonvirtual String ToUpperCase () const;
1083
1084 public:
1085 /**
1086 * Return true if the string contains zero non-whitespace characters.
1087 */
1088 nonvirtual bool IsWhitespace () const;
1089
1090 public:
1091 /**
1092 * \brief return the first maxLen (or fewer if string shorter) characters of this string (adding ellipsis if truncated)
1093 *
1094 * This function is for GUI/display purposes. It returns the given string, trimmed down
1095 * to at most maxLen characters, and removes whitespace (on 'to trim' side - given by keepLeft flag -
1096 * if needed to get under maxLen).
1097 *
1098 * Note in the 3-arg overload, the ellipsis string MAY be the empty string.
1099 */
1100 nonvirtual String LimitLength (size_t maxLen, StringShorteningPreference keepPref = StringShorteningPreference::ePreferKeepLeft) const;
1101 nonvirtual String LimitLength (size_t maxLen, StringShorteningPreference keepLeft, const String& ellipsis) const;
1102
1103 public:
1104 /**
1105 * CopyTo () copies the contents of this string to the target buffer.
1106 * CopyTo () does NOT nul-terminate the target buffer
1107 *
1108 * Returns span of CHAR_T objects written - a subspan of the argument span
1109 *
1110 * \pre s.size () >= UTFConvert::ComputeTargetBufferSize<CHAR_T> (...this-string-data...);
1111 *
1112 * \see See also GetData<CHAR_T> (buf) - similar functionality - except caller doesn't need to know size of buffer to allocate
1113 */
1114 template <IUNICODECanAlwaysConvertTo CHAR_T>
1116 requires (not is_const_v<CHAR_T>);
1117
1118 public:
1119 /**
1120 * Convert String losslessly into a standard C++ type.
1121 *
1122 * Only specifically specialized variants supported: IBasicUNICODEStdString<T> or same_as<T,String>
1123 * o wstring
1124 * o u8string
1125 * o u16string
1126 * o u32string
1127 * o String (return *this; handy sometimes in templated usage; harmless)
1128 * as well as:
1129 * o filesystem::path (or anything with .wstring() -> wstring method) - note see qStroika_Foundation_Characters_AsPathAutoMapMSYSAndCygwin
1130 *
1131 * DEPRECATED AS OF v3.0d1 because As is const method - could do non-const As<> overload for these, but that would be confusing
1132 * o const wchar_t*
1133 * o const Character*
1134 *
1135 * \note
1136 * o As<u8string> () equivalent to AsUTF8 () call
1137 * o As<u16string> () equivalent to AsUTF16 () call
1138 * o As<u32string> () equivalent to AsUTF32 () call
1139 *
1140 * \note We tried to also have template<typename T> explicit operator T () const; - conversion operator - but
1141 * We got too frequent confusion in complex combinations of templates, like with:
1142 * Set<String> x ( *optional<String> {String{}) ); // fails cuz calls operator Set<String> ()!
1143 * Set<String> x { *optional<String> {String{}) }; // works as expected
1144 */
1145 template <typename T>
1146 nonvirtual T As () const
1148
1149 public:
1150 /**
1151 * Create a narrow string object from this, based on the encoding from the argument locale.
1152 * This throws an exception if there is an error performing the conversion, and the 'into' overload
1153 * leaves 'into' in an undefined (but safe) state.
1154 */
1155 nonvirtual string AsNarrowString (const locale& l) const;
1157
1158 public:
1159 /**
1160 * Convert String losslessly into a standard C++ type.
1161 * Only specifically specialized variants are supported.
1162 *
1163 * SUPPORTED result type "T": values are:
1164 * string
1165 * u8string
1166 */
1167 template <typename T = u8string>
1168 nonvirtual T AsUTF8 () const
1169 requires (same_as<T, string> or same_as<T, u8string>);
1170
1171 public:
1172 /**
1173 * Convert String losslessly into a standard C++ type u16string.
1174 *
1175 * \par Example Usage:
1176 * \code
1177 * String s = u"hi mom";
1178 * u16string su = AsUTF16 ();
1179 * \endcode
1180 *
1181 * \note - the resulting string may have a different length than this->size() due to surrogates
1182 *
1183 * @todo allow wchar_t if sizeof(wchar_t) == 2
1184 */
1185 template <typename T = u16string>
1186 nonvirtual T AsUTF16 () const
1187 requires (same_as<T, u16string> or (sizeof (wchar_t) == sizeof (char16_t) and same_as<T, wstring>));
1188
1189 public:
1190 /**
1191 * Convert String losslessly into a standard C++ type u32string.
1192 *
1193 * \par Example Usage:
1194 * \code
1195 * String s = u"hi mom";
1196 * u32string su = AsUTF32 ();
1197 * \endcode
1198 *
1199 * \note - As of Stroika 2.1d23 - the resulting string may have a different length than this->size() due to surrogates,
1200 * but eventually the intent is to fix Stroika's string class so this is not true, and it returns the length of the string
1201 * in size () with surrogates removed (in other words uses ucs32 representation). But not there yet.
1202 */
1203 template <typename T = u32string>
1204 nonvirtual T AsUTF32 () const
1205 requires (same_as<T, u32string> or (sizeof (wchar_t) == sizeof (char32_t) and same_as<T, wstring>));
1206
1207 public:
1208 /**
1209 * See docs on SDKChar for meaning (character set).
1210 *
1211 * Note - many UNICODE Strings cannot be represented in the SDKString character set (especially if narrow - depends a lot).
1212 * But in that case, AsNarrowSDKString () will throw, unless AllowMissingCharacterErrorsFlag is specified.
1213 */
1214 nonvirtual SDKString AsSDKString () const;
1216
1217 public:
1218 /**
1219 * See docs on SDKChar for meaning (character set). If SDKChar is a wide character, there is probably still a
1220 * default 'code page' to interpret narrow characters (Windows CP_ACP). This is a string in that character set.
1221 *
1222 * Note - many UNICODE Strings cannot be represented in the SDKString character set (especially if narrow - depends a lot).
1223 * But in that case, AsNarrowSDKString () will throw, unless AllowMissingCharacterErrorsFlag is specified.
1224 */
1225 nonvirtual string AsNarrowSDKString () const;
1227
1228 public:
1229 /**
1230 * Convert String losslessly into a standard C++ type.
1231 * Only specifically specialized variants are supported (right now just <string> supported).
1232 * The source string MUST be valid ascii characters - throw Execution::Exception<runtime_error>
1233 *
1234 * \par Example Usage:
1235 * \code
1236 * string a1 = String{"hi mom"}.AsASCII (); // OK
1237 * string a2 = String{u"שלום"}.AsASCII (); // throws
1238 * \endcode
1239 *
1240 * \note - this is a (compatible) change of behavior: before Stroika v2.1d23, this would assert out on invalid ASCII.
1241 *
1242 * Supported Types:
1243 * o Memory::StackBuffer<char>
1244 * o string
1245 * o u8string (note any ASCII string is also legit utf-8)
1246 */
1247 template <typename T = string>
1248 nonvirtual T AsASCII () const
1249 requires requires (T* into) {
1250 { into->empty () } -> same_as<bool>;
1251 { into->push_back (ASCII{0}) };
1252 };
1253
1254 public:
1255 /**
1256 * Convert String losslessly into a standard C++ type.
1257 * Only specifically specialized variants are supported (right now just <string> supported).
1258 * If this source contains any invalid ASCII characters, this returns nullopt, and else a valid engaged string.
1259 *
1260 * Supported Types(T):
1261 * o Memory::StackBuffer<char>
1262 * o string
1263 * o u8string (note any valid ASCII string is also valid utf-8)
1264 */
1265 template <typename T = string>
1267 requires requires (T* into) {
1268 { into->empty () } -> same_as<bool>;
1269 { into->push_back (ASCII{0}) };
1270 };
1271
1272 public:
1273 /**
1274 * \brief Summary data for raw contents of rep - each rep will support at least one of these span forms
1275 *
1276 * Each rep will support a span of at least one code-point type (ascii, utf8, utf16, or utf32)
1277 *
1278 * This API is guaranteed to support a span of at least one of these types (maybe more). The caller may
1279 * specify the code-point type preferred.
1280 *
1281 * \note eAscii is a subset of eSingleByteLatin1, so when the type eAscii is returned, EITHER fSingleByteLatin1 or fAscii maybe
1282 * maybe used.
1283 *
1284 * This API is public, but best to avoid depending on internals of String API - like PeekSpanData - since
1285 * this reasonably likely to change in future versions.
1286 */
1289 /**
1290 * ASCII is useful to track in storage (though same size as eSingleByteLatin1) - because requests
1291 * to convert to UTF-8 are free - ASCII is legit UTF8 (not true for eSingleByteLatin1)
1292 */
1294 /**
1295 * Latin1 - 8 bit representation of characters. But 256 of them - more than plain ascii.
1296 * And cheap/easy to convert to UNICODE (since code points of wider characters exactly the same values).
1297 */
1299 eChar16,
1300 eChar32
1301 };
1303 union {
1304 span<const ASCII> fAscii;
1305 span<const Latin1> fSingleByteLatin1;
1306 span<const char16_t> fChar16;
1307 span<const char32_t> fChar32;
1308 };
1309 };
1310
1311 public:
1312 /**
1313 * \brief return the constant character data inside the string in the form of a case variant union of different span types (at least one will be there)
1314 * templated type arg just used to pick a preferred type.
1315 *
1316 * \note CHAR_TYPE == char implies eAscii
1317 *
1318 * \note Reason for the two step API - getting the PeekSpanData, and then using - is because getting
1319 * the data is most expensive part (virtual function), and the packaged PeekSpanData gives enuf
1320 * info to do the next steps (quickly inline usually)
1321 *
1322 * This API is public, but best to avoid depending on internals of String API - like PeekSpanData - since
1323 * this reasonably likely to change in future versions.
1324 */
1325 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE = ASCII>
1326 nonvirtual PeekSpanData GetPeekSpanData () const;
1327
1328 public:
1329 /**
1330 * \brief return the constant character data inside the string in the form of a span or nullopt if not available for that CHAR_TYPE
1331 *
1332 * This API is public, but best to avoid depending on internals of String API - like PeekSpanData - since
1333 * this reasonably likely to change in future versions.
1334 *
1335 * \note It is generally true that the data IsASCII (span) IFF Peek<ASCII> returns non-nullopt. But this is
1336 * not ACTUALLY always true. Generally, Stroika constructs strings like this. But callers may manually construct
1337 * a String with backend rep u32string, for example (e.g because of move construct) - and that might just happen
1338 * to be all ascii. You can count on that IF you get back value from PeekData<ASCII> - it must be all ASCII. But
1339 * the contrapositive is not always true.
1340 */
1341 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
1343 template <IUNICODECanUnambiguouslyConvertFrom CHAR_TYPE>
1344 nonvirtual optional<span<const CHAR_TYPE>> PeekData () const;
1345
1346 public:
1347 /**
1348 * \brief return the constant character data inside the string (rep) in the form of a span, possibly quickly and directly, and possibly copied into possiblyUsedBuffer
1349 *
1350 * This API will typically return a span of data which is internal pointers into the data of the rep (and so its invalidated on the
1351 * next change to the string).
1352 *
1353 * BUT - it maybe a span of data stored into the argument possiblyUsedBuffer (which is why it must be provided - cannot be nullptr).
1354 * If you want the freedom to not pass in this buffer, see the PeekData API.
1355 *
1356 * \note - CHAR_T must satisfy the concept IUNICODECanAlwaysConvertTo - SAFELY - because the string MIGHT contain characters not in any
1357 * unsafe char class (like ASCII or Latin1), and so there might not be a way to do the conversion. Use
1358 * PeekData () to do that - where it can return nullopt if no conversion possible.
1359 *
1360 * \par Example Usage
1361 * \code
1362 * Memory::StackBuffer<char8_t> maybeIgnoreBuf1;
1363 * span<const char8_t> thisData = GetData (&maybeIgnoreBuf1);
1364 * \endcode
1365 *
1366 * \note Prior to Stroika v3.0d1, GetData() took no arguments, and returned pair<const CHAR_TYPE*, const CHAR_TYPE*>
1367 * which is pretty similar, but not quite the same. To adapt any existing code calling that older version of the API
1368 * just add a Memory::StackBuffer<T> b; and pass &b to GetData(); And the return span is not the same as pair<> but
1369 * easily convertible.
1370 */
1371 template <IUNICODECanAlwaysConvertTo CHAR_TYPE, size_t STACK_BUFFER_SZ>
1373 template <IUNICODECanAlwaysConvertTo CHAR_TYPE, size_t STACK_BUFFER_SZ>
1375
1376 public:
1377 struct EqualsComparer;
1378
1379 public:
1380 struct LessComparer;
1381
1382 public:
1383 struct ThreeWayComparer;
1384
1385 public:
1386 /**
1387 * Return true if case sensitive compare of the two IConvertibleToString objects have the same characters.
1388 * Indirects to EqualsComparer{eWithCase} (...)
1389 *
1390 * \note For reasons I don't understand, the plain function declaration of operator== and requires not do appear to be required
1391 * on all major compilers due to quirks of the operator= rewrite rules, but I don't fully understand why --LGP 2024-11-18.
1392 */
1393 nonvirtual bool operator== (const String& rhs) const;
1394 template <IConvertibleToString T>
1395 nonvirtual bool operator== (T&& rhs) const
1397
1398 public:
1399 /**
1400 * Return strong_ordering of case sensitive (three-way) compare of the two IConvertibleToString objects.
1401 * Indirects to ThreeWayComparer{eWithCase} (...)
1402 *
1403 * \see operator== ()
1404 */
1405 nonvirtual strong_ordering operator<=> (const String& rhs) const;
1406 template <IConvertibleToString T>
1407 nonvirtual strong_ordering operator<=> (T&& rhs) const
1409
1410 public:
1411 /**
1412 * @aliases basic_string>char>::npos
1413 *
1414 * This is only used for 'STL-compatibility APIs, like substr (), find, rfind (), etc.
1415 */
1416 static constexpr size_t npos = static_cast<size_t> (-1);
1417
1418 public:
1419 /**
1420 * @aliases size
1421 */
1422 nonvirtual size_t length () const noexcept;
1423
1424 public:
1425 /**
1426 * \note BREAKING change between Stroika 2.1 and v3 - const c_str/0 no longer guaranteed to return non-null
1427 *
1428 * Mitigating this, the non-const c_str() still will return non-null, and the const overload taking
1429 * StackBuffer<wchar_t> will also guarantee returning non-null.
1430 *
1431 * In the case of the overloads taking no arguments, the lifetime of the returned pointer is until the
1432 * next change to this string. In the case of the StackBuffer overload, the guarantee extends for the lifetime
1433 * of the argument buffer (typically just the next few lines of code).
1434 *
1435 * This will always return a value which is NUL-terminated.
1436 *
1437 * Note also - the c_str () function CAN now be somewhat EXPENSIVE, causing a mutation of the String object, so use
1438 * one of the const overloads where possible (or where performance matters).
1439 *
1440 * \note Why does c_str (StackBuffer) return a tuple?
1441 * Sometimes you just want a plain const wchar_t* you can use with an old C pointer based API. But that
1442 * fails/asserts out if you happen to have an empty string and try to get the pointer. Sometimes - you just need
1443 * the pointer!
1444 *
1445 * And why the string-view part? Because sometimes you want the LENGTH. Sure - you can just compute it again. But
1446 * that is costly. Sure you can just use the original string length. BUT THAT WOULD BE A BUG once I support
1447 * surrogates properly (at least on windows where wchar_t isn't char32_t).
1448 */
1449 nonvirtual tuple<const wchar_t*, wstring_view> c_str (Memory::StackBuffer<wchar_t>* possibleBackingStore) const;
1450
1451 public:
1452 /**
1453 * Follow the basic_string<>::find () API
1454 *
1455 * need more overloads.
1456 *
1457 * Returns String::npos if not found, else the zero based index.
1458 */
1459 nonvirtual size_t find (Character c, size_t startAt = 0) const;
1460 nonvirtual size_t find (const String& s, size_t startAt = 0) const;
1461
1462 public:
1463 /**
1464 * Follow the basic_string<>::rfind () API
1465 *
1466 * need more overloads.
1467 *
1468 * Returns String::npos if not found, else the zero based index.
1469 */
1470 nonvirtual size_t rfind (Character c) const;
1471
1472 public:
1473 /**
1474 * mimic https://en.cppreference.com/w/cpp/string/basic_string/front
1475 *
1476 * \pre not empty ()
1477 */
1478 nonvirtual Character front () const;
1479
1480 public:
1481 /**
1482 * mimic https://en.cppreference.com/w/cpp/string/basic_string/back
1483 *
1484 * \pre not empty ()
1485 */
1486 nonvirtual Character back () const;
1487
1488 public:
1489 /**
1490 * Compatable with STL::basic_string::subtr() - which interprets second argument as count. Not the same
1491 * as Stroika::String::SubString (where the second argument is a 'to')
1492 *
1493 * @see SubString
1494 *
1495 * From http://en.cppreference.com/w/cpp/string/basic_string/substr
1496 * Returns a substring [pos, pos+count). If the requested substring extends
1497 * past the end of the string, or if count == npos, the returned substring is [pos, size()).
1498 * std::out_of_range if pos > size()
1499 */
1500 nonvirtual String substr (size_t from, size_t count = npos) const;
1501
1502 public:
1503 ///////////////// DEPRECATED FUNCTIONS /////////////////
1504 [[deprecated ("Since Stroika v3.0d13 - if you must use c_str() - use the overload taking StackBuffer arg), or use As<wstring> "
1505 "().c_str ()")]] const wchar_t*
1506 c_str ();
1507 [[deprecated ("Since Stroika v3.0d12 use StringBuilder::SetAt")]] void SetCharAt (Character c, size_t i);
1508 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void erase (size_t from = 0);
1509 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void erase (size_t from, size_t count);
1510 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void push_back (wchar_t c);
1511 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void push_back (Character c);
1512 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void Append (Character c);
1513 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void Append (const String& s);
1514 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void Append (const wchar_t* s);
1515 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void Append (const wchar_t* from, const wchar_t* to);
1516 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void Append (const Character* from, const Character* to);
1518 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] void Append (span<const CHAR_T> s)
1520 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] String& operator+= (Character appendage);
1521 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] String& operator+= (const String& appendage);
1522 [[deprecated ("Since Stroika v3.0d12 use StringBuilder")]] String& operator+= (const wchar_t* appendageCStr);
1523 [[deprecated ("Since Stroika v3.0d12 - just use a b String{}")]] void clear ()
1524 {
1525 *this = String{};
1526 }
1527 template <typename T>
1528 [[deprecated ("Since Stroika v3.0d2, just use 0 arg version)")]] void As (T* into) const
1529 requires (IBasicUNICODEStdString<T> or same_as<T, String>)
1530 {
1531 *into = this->As<T> ();
1532 }
1533 [[deprecated ("Since Stroika v3.0d2, just use 1 arg version)")]] void AsNarrowString (const locale& l, string* into) const
1534 {
1535 *into = this->AsNarrowString (l);
1536 }
1537 template <typename T = u8string>
1538 [[deprecated ("Since Stroika v3.0d2 - use AsUTF8/0")]] void AsUTF8 (T* into) const
1539 requires (same_as<T, string> or same_as<T, u8string>)
1540 {
1541 *into = this->AsUTF8 ();
1542 }
1543 template <typename T = u16string>
1544 [[deprecated ("Since Stroika v3.0d2 - use AsUTF16/0")]] void AsUTF16 (T* into) const
1545 requires (same_as<T, u16string> or (sizeof (wchar_t) == sizeof (char16_t) and same_as<T, wstring>))
1546 {
1547 *into = AsUTF16 ();
1548 }
1549 template <typename T = u32string>
1550 [[deprecated ("Since Stroika v3.0d2 - use AsUTF32/0")]] void AsUTF32 (T* into) const
1551 requires (same_as<T, u32string> or (sizeof (wchar_t) == sizeof (char32_t) and same_as<T, wstring>))
1552 {
1553 *into = AsUTF32 ();
1554 }
1555 [[deprecated ("Since Stroika v3.0d2 - just use /0")]] void AsSDKString (SDKString* into) const
1556 {
1557 *into = AsSDKString ();
1558 }
1559 [[deprecated ("Since Stroika v3.0d2 - just use /0")]] void AsNarrowSDKString (string* into) const
1560 {
1561 *into = SDK2Narrow (AsSDKString ());
1562 }
1563 template <typename T = string>
1564 [[deprecated ("Since v3.0d2 use /0")]] void AsASCII (T* into) const
1565 requires (same_as<T, string> or same_as<T, Memory::StackBuffer<char>>)
1566 {
1567 if (not AsASCIIQuietly (into)) {
1568 ThrowInvalidAsciiException_ ();
1569 }
1570 }
1571 template <typename T = string>
1572 [[deprecated ("Since v3.0d2 use /0 overload")]] bool AsASCIIQuietly (T* into) const
1573 requires (same_as<T, string> or same_as<T, Memory::StackBuffer<char>>)
1574 {
1575 auto r = this->AsASCIIQuietly ();
1576 if (r) {
1577 *into = *r;
1578 return true;
1579 }
1580 else {
1581 return false;
1582 }
1583 }
1584 [[deprecated ("Since Stroika v3.0d5 use StringShorteningPreference argument")]] String LimitLength (size_t maxLen, bool keepLeft) const
1585 {
1586 return LimitLength (maxLen, keepLeft ? StringShorteningPreference::ePreferKeepLeft : StringShorteningPreference::ePreferKeepRight);
1587 }
1588 [[deprecated ("Since Stroika v3.0d5 use StringShorteningPreference argument")]] String LimitLength (size_t maxLen, bool keepLeft,
1589 const String& ellipsis) const
1590 {
1591 return LimitLength (maxLen, keepLeft ? StringShorteningPreference::ePreferKeepLeft : StringShorteningPreference::ePreferKeepRight, ellipsis);
1592 }
1593 template <typename CHAR_T>
1594 [[deprecated ("Since Stroika v3.0d1, String{}")]] static String FromASCII (span<const CHAR_T> s)
1595 {
1596 return String{s};
1597 }
1598 template <typename CHAR_T>
1599 [[deprecated ("Since Stroika v3.0d1, String{}")]] static String FromASCII (const CHAR_T* cString)
1600 {
1601 return String{cString};
1602 }
1603 template <IStdBasicStringCompatibleCharacter CHAR_T>
1604 [[deprecated ("Since Stroika v3.0d1, String{}")]] static String FromASCII (const basic_string<CHAR_T>& str)
1605 {
1606 return String{str};
1607 }
1608 [[deprecated ("Since Stroika v3.0d1, use span{} overload for this")]] static String FromASCII (const char* from, const char* to)
1609 {
1610 return String{span{from, to}};
1611 }
1612 [[deprecated ("Since Stroika v3.0d1, use span{} overload for this")]] static String FromASCII (const wchar_t* from, const wchar_t* to)
1613 {
1614 return String{span{from, to}};
1615 }
1616 [[deprecated ("Since Stroika v3.0d1, use span overloads")]] String InsertAt (const wchar_t* from, const wchar_t* to, size_t at) const
1617 {
1618 Memory::StackBuffer<Character> buf{Memory::eUninitialized, UTFConvert::ComputeTargetBufferSize<Character> (span{from, to})};
1619 return InsertAt (UTFConvert::kThe.ConvertSpan (span{from, to}, span{buf}), at);
1620 }
1621 [[deprecated ("Since Stroika v3.0d1, use span overloads")]] String InsertAt (const Character* from, const Character* to, size_t at) const
1622 {
1623 return InsertAt (span{from, to}, at);
1624 }
1625 [[deprecated ("Since Stroika v3.0d1, use span{} overload for this")]] static String FromLatin1 (const char* start, const char* end)
1626 {
1627 return FromLatin1 (span{start, end});
1628 }
1629 [[deprecated ("Since Stroika v3.0d1, use span{} constructor for this")]] static String FromNarrowString (const char* from,
1630 const char* to, const locale& l)
1631 {
1632 return FromNarrowString (span{from, to}, l);
1633 }
1634 [[deprecated ("Since Stroika v3.0d1, use span{} constructor for this")]] static String FromNarrowSDKString (const char* from, const char* to)
1635 {
1636 return FromNarrowSDKString (span{from, to});
1637 }
1638 template <IUNICODECanAlwaysConvertTo CHAR_T>
1639 [[deprecated ("Since Stroika v3.0d1, use span{} constructor for this")]] String (const CHAR_T* from, const CHAR_T* to)
1640 : String{span<const CHAR_T>{from, to}}
1641 {
1642 }
1643 [[deprecated (
1644 "Since Stroika v3.0d1 - use As<wstring> ().c_str () or other c_str() overload (*UNSAFE TO USE*)")]] nonvirtual const wchar_t*
1645 c_str () const noexcept;
1646 [[deprecated ("Since Stroika v3.0 - use span{} overloads")]] inline static String FromSDKString (const SDKChar* from, const SDKChar* to)
1647 {
1648 return FromSDKString (span{from, to});
1649 }
1650 [[deprecated ("Since Stroika v3.0 - use span{} overloads")]] static String FromUTF8 (const char* from, const char* to)
1651 {
1652 return FromUTF8 (span{from, to});
1653 }
1654 [[deprecated ("Since Stroika v3.0 - use span{} overloads")]] static String FromUTF8 (const char8_t* from, const char8_t* to)
1655 {
1656 return FromUTF8 (span{from, to});
1657 }
1658 template <typename T = string>
1659 [[deprecated ("Since Stroika v3.0d1 - use Character::AsAsciiQuietly")]] static bool AsASCIIQuietly (const wchar_t* fromStart,
1660 const wchar_t* fromEnd, T* into)
1661 {
1662 return Character::AsASCIIQuietly (span<const wchar_t>{fromStart, fromEnd}, into);
1663 }
1664 [[deprecated ("Since Stroika v3.0d1 due to https://github.com/SophistSolutions/Stroika/issues/1097 (STK-965) - NOT "
1665 "IMPLEMENTED")]] nonvirtual const wchar_t*
1666 data () const;
1667 [[deprecated ("Since Stroika v3.0d8 - use RemoveFirstIf")]] String Remove (Character c) const
1668 {
1669 return RemoveFirstIf (c);
1670 }
1671 [[deprecated ("Since Stroika v3.0d8 - use RemoveFirstIf")]] String Remove (const String& subString) const
1672 {
1673 return RemoveFirstIf (subString);
1674 }
1675
1676 private:
1677 static shared_ptr<_IRep> mkEmpty_ ();
1678
1679 private:
1680 /**
1681 * If the argument CHAR_T is restrictive (such as ASCII/char) - this CHECKS and THROWS (Character::CheckASCII).
1682 * This function also reads the data, and sees if it can downshift 'CHAR_T' to something more restrictive, and produces
1683 * a possibly smaller rep.
1684 *
1685 * For some overloads (e..g && move) - the data is 'stolen/moved'.
1686 *
1687 * See mk_nocheck_ for a simpler - DO WHAT I SAID - operation.
1688 */
1689 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
1690 static shared_ptr<_IRep> mk_ (span<const CHAR_T> s);
1691 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
1692 static shared_ptr<_IRep> mk_ (Iterable<CHAR_T> it);
1693 template <IUNICODECanUnambiguouslyConvertFrom CHAR_T>
1694 static shared_ptr<_IRep> mk_ (span<CHAR_T> s);
1695 template <IStdBasicStringCompatibleCharacter CHAR_T>
1696 static shared_ptr<_IRep> mk_ (basic_string<CHAR_T>&& s);
1697
1698 private:
1699 /*
1700 * Note the mk_nocheck_ - just does the mk of the buffer, but assuming the arguments are legit and will fit (though it may
1701 * assert in DEBUG builds this is true).
1702 *
1703 * This just blindly allocates the buffer of the given size/type for the given arguments.
1704 */
1705 template <typename CHAR_T>
1706 static shared_ptr<_IRep> mk_nocheck_ (span<const CHAR_T> s)
1708
1709 private:
1710 template <unsigned_integral T>
1711 nonvirtual size_t SubString_adjust_ (T fromOrTo, size_t myLength) const;
1712 template <signed_integral T>
1713 nonvirtual size_t SubString_adjust_ (T fromOrTo, size_t myLength) const;
1714
1715 private:
1716 nonvirtual String SubString_ (const _SafeReadRepAccessor& thisAccessor, size_t from, size_t to) const;
1717
1718 protected:
1719 nonvirtual void _AssertRepValidType () const;
1720
1721 private:
1722 [[noreturn]] static void ThrowInvalidAsciiException_ (); // avoid include
1723 };
1724 static_assert (totally_ordered<String>);
1725
1726#if qStroika_Foundation_Characters_AsPathAutoMapMSYSAndCygwin
1727 template <>
1728 std::filesystem::path String::As<std::filesystem::path> () const;
1729#endif
1730
1731 /**
1732 * operator<< ostream adapters work as you would expect and allow writing Stroika strings easily to ostreams such as cout.
1733 *
1734 * \note uses AsNarrowSDKString (eIgnoreErrors)
1735 *
1736 * The only catch - is that Stroika strings are UNICODE based, and so may not fit perfectly with 'char' based basic_ostream<>.
1737 * To address this, Stroika strings are mapped to 'narrow sdk strings' - ignoring any errors. As this is generally not a very
1738 * good practice to do (lossy) - and generally just done for debugging/diagnostic output, this was deemed acceptable (as of Stroika v3.0d6).
1739 */
1740 wostream& operator<< (wostream& out, const String& s);
1741 ostream& operator<< (ostream& out, const String& s);
1742
1743#if qStroika_HasComponent_googletest
1744 // For googletest compatibility
1745 void PrintTo (const String& s, std::ostream* os);
1746#endif
1747
1748 /**
1749 * Protected helper Rep class.
1750 *
1751 * \note Important design note - String reps are IMMUTABLE. Changes to string like +=, create new string reps (so costly).
1752 * Use StringBuilder for that purpose in performance sensitive code.
1753 */
1754 class String::_IRep : public Iterable<Character>::_IRep {
1755 public:
1756 /**
1757 * Return the ith character in the string.
1758 */
1759 virtual Character GetAt (size_t index) const noexcept = 0;
1760
1761 public:
1762 /**
1763 * Each rep will support a span of at least one code-point type (ascii, utf8, utf16, or utf32)
1764 *
1765 * This API is guaranteed to support a span of at least one of these types (maybe more). The caller may
1766 * specify the code-point type preferred.
1767 */
1769
1770 public:
1771 /*
1772 * Return a pointer to mostly standard (wide, nul-terminated) C string,
1773 * whose lifetime extends to the next non-const call on this rep, or nullptr.
1774 *
1775 * It is only 'mostly' standard because it is allowed to have nul-chars embedded in it. But it will
1776 * always have str[len] == 0;
1777 *
1778 * \note Since Stroika v3.0d1, this can return nullptr (in which case the String library will allocate a new backend)
1779 *
1780 * \post returnResult == nullptr or returnResult[len] == '\0';
1781 */
1782 virtual const wchar_t* c_str_peek () const noexcept = 0;
1783
1784 private:
1786 };
1787
1788 // Some some docs/testing...
1790 static_assert (not IConvertibleToString<char>); // would have been sensible to allow, but easily generates confusing results: cuz that means String x = 3 would work; confusing with ovarloads)
1797
1798 namespace Private_ {
1799 // This is just anything that can be treated as a 'span<const Character>'
1800 // clang-format off
1801 template <typename T>
1802 concept ICanBeTreatedAsSpanOfCharacter_ =
1804 or same_as<remove_cvref_t<T>, u8string>
1805 or same_as<remove_cvref_t<T>, u8string_view>
1806 or same_as<remove_cvref_t<T>, u16string>
1807 or same_as<remove_cvref_t<T>, u16string_view>
1808 or same_as<remove_cvref_t<T>, u32string>
1809 or same_as<remove_cvref_t<T>, u32string_view>
1810 or same_as<remove_cvref_t<T>, wstring>
1811 or same_as<remove_cvref_t<T>, wstring_view>
1813 or same_as<remove_cvref_t<T>, const char8_t*>
1814 or same_as<remove_cvref_t<T>, const char16_t*>
1815 or same_as<remove_cvref_t<T>, const char32_t*>
1816 or same_as<remove_cvref_t<T>, const wchar_t*>
1817 ;
1818 // clang-format on
1819
1820 template <ICanBeTreatedAsSpanOfCharacter_ USTRING, size_t STACK_BUFFER_SZ>
1822 }
1823
1824 /**
1825 *
1826 * \par Example Usage
1827 * \code
1828 * constexpr String::EqualsComparer kStringCIComparer_ {Characters::CompareOptions::eCaseInsensitive};
1829 * if (kStringCIComparer_ (filename.extension (), ".HFCC"sv)) {
1830 * compiledName = filename;
1831 * }
1832 * \endcode
1833 *
1834 * \note There is no String::Equals() method, because it would look queer if it took one string argument, and if it was static
1835 * it would essentially look like the above comparer, so little point.
1836 */
1837 struct String::EqualsComparer : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eEquals> {
1838 /**
1839 * optional CompareOptions to CTOR allows for case insensitive compares
1840 */
1841 constexpr EqualsComparer (CompareOptions co = eWithCase);
1842
1843 /**
1844 * Extra overloads a slight performance improvement
1845 */
1846 template <IConvertibleToString LT, IConvertibleToString RT>
1847 nonvirtual bool operator() (LT&& lhs, RT&& rhs) const;
1848
1849 CompareOptions fCompareOptions;
1850
1851 private:
1852 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1853 bool Cmp_ (LT&& lhs, RT&& rhs) const;
1854 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1855 bool Cmp_Generic_ (LT&& lhs, RT&& rhs) const;
1856 };
1857
1858 /**
1859 */
1860 struct String::ThreeWayComparer : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eThreeWayCompare> {
1861 /**
1862 * optional CompareOptions to CTOR allows for case insensitive compares
1863 */
1864 constexpr ThreeWayComparer (CompareOptions co = eWithCase);
1865
1866 /**
1867 * Extra overloads a slight performance improvement
1868 */
1869 template <IConvertibleToString LT, IConvertibleToString RT>
1870 nonvirtual strong_ordering operator() (LT&& lhs, RT&& rhs) const;
1871
1872 CompareOptions fCompareOptions;
1873
1874 private:
1875 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1876 strong_ordering Cmp_ (LT&& lhs, RT&& rhs) const;
1877 template <Private_::ICanBeTreatedAsSpanOfCharacter_ LT, Private_::ICanBeTreatedAsSpanOfCharacter_ RT>
1878 strong_ordering Cmp_Generic_ (LT&& lhs, RT&& rhs) const;
1879 };
1880
1881 /**
1882 * \brief very similar to ThreeWayComparer but returns true if less
1883 */
1884 struct String::LessComparer : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eStrictInOrder> {
1885 constexpr LessComparer (CompareOptions co = eWithCase);
1886
1887 template <typename T1, typename T2>
1888 nonvirtual bool operator() (T1 lhs, T2 rhs) const;
1889
1890 private:
1891 ThreeWayComparer fComparer_;
1892 };
1894
1895 inline namespace Literals {
1896 /**
1897 * \brief shorthand for String::FromStringConstant { ARGUMENT }
1898 *
1899 * \par Example:
1900 * \code
1901 * String s1 = "some-string"_k;
1902 * String s2 = String::FromStringConstant ("some-string");
1903 * String s3 = "some-string"sv; // in most cases this will also work fine, and is preferable (since sv is part of C++ standard)
1904 * \endcode
1905 *
1906 * \note _k is STILL sometimes useful and better than sv, since the TYPE returned by _k is a String, so it will work in
1907 * some overload contexts where sv would fail - and it skips the check a basic_string_view constructor makes (that its
1908 * characters are a literal's), so is a little cheaper.
1909 *
1910 * \note operator""_k with char*, requires that the argument string MUST BE ASCII (someday maybe lifted to allow Latin1),
1911 * checked by assertion only (Debug builds), as for FromStringConstant
1912 */
1913 String operator""_k (const ASCII* s, size_t len);
1914 String operator""_k (const wchar_t* s, size_t len);
1915 String operator""_k (const char8_t* s, size_t len);
1916 String operator""_k (const char16_t* s, size_t len);
1917 String operator""_k (const char32_t* s, size_t len);
1918 }
1919
1920 /**
1921 * Basic operator overload with the obvious meaning, and simply indirect to @String::Concatenate (const String& rhs)
1922 *
1923 * \note Design Note
1924 * Don't use member function so "x" + String{u"x"} works.
1925 * Insist that EITHER LHS or RHS is a string (else operator applies too widely).
1926 *
1927 * Both arguments must be convertible to a String, and at least must be String or derived from String
1928 */
1929 template <IConvertibleToString LHS_T, IConvertibleToString RHS_T>
1932
1933 /**
1934 * \brief StringCombiner is a simple function object used to combine two strings visually - used in Iterable<>::Join ()
1935 *
1936 * This can combine strings in the obvious way (concatenation) - but defaults to separating them with a comma (', ').
1937 *
1938 * \note the functional api - is to be given two strings, and a flag saying if the combination is the last one in the list,
1939 * since in English, this is frequently rendered somewhat differently than the rest.
1940 */
1941 template <typename STRING = String>
1943 STRING fSeparator{", "sv};
1944 optional<STRING> fSpecialSeparatorForLastPair;
1945 STRING operator() (const STRING& lhs, const STRING& rhs, bool isLast) const;
1946 };
1947
1948 /**
1949 * kDefaultStringCombiner is just StringCombiner{}, rendered as a function object, so that it can be externed/imported
1950 * in the Iterable code without imposing a dependency on the String code.
1951 */
1952 extern const function<String (String, String, bool)> kDefaultStringCombiner;
1953
1954}
1955
1956namespace Stroika::Foundation::Traversal {
1957 // specialized as performance optimization
1958 template <>
1959 Characters::String Iterable<Characters::String>::Join (const Characters::String& separator, const optional<Characters::String>& finalSeparator) const;
1960}
1961
1962namespace std {
1963 template <>
1964 struct hash<Stroika::Foundation::Characters::String> {
1965 size_t operator() (const Stroika::Foundation::Characters::String& arg) const;
1966 };
1967}
1968
1969namespace Stroika::Foundation::Memory {
1970 class BLOB; // Forward declare to avoid mutual include issues
1971}
1972
1974 template <typename T>
1975 struct DefaultSerializer; // Forward declare to avoid mutual include issues
1976 template <>
1978 Memory::BLOB operator() (const Stroika::Foundation::Characters::String& arg) const;
1979 };
1980}
1981
1982/**
1983 * Allow std::format to work with String class
1984 *
1985 * \note These specializations are written inside namespace qStroika_Foundation_Characters_FMT_PREFIX_ { ... }, NOT with
1986 * the equivalent (and perfectly legal) qualified form:
1987 * template <> struct qStroika_Foundation_Characters_FMT_PREFIX_::formatter<String, wchar_t> {...};
1988 * The qualified form crashes the VSCode C/C++ extension's IntelliSense process (cpptools 1.34.4, windows-msvc-x64:
1989 * "IntelliSense process crash detected: handle_initialize") whenever this header is open in an editor and any
1990 * translation unit that includes it is parsed. The extension then silently falls back to the tag parser, so there
1991 * is no hover, member completion, or go-to-definition for anything using String (found 2026-09-28).
1992 */
1994 template <>
1995 struct formatter<Stroika::Foundation::Characters::String, wchar_t> {
1996 qStroika_Foundation_Characters_FMT_PREFIX_::formatter<std::wstring, wchar_t> fDelegate2_;
1997
1998 template <typename ParseContext>
1999 constexpr typename ParseContext::iterator parse (ParseContext& ctx)
2000 {
2001#if qCompilerAndStdLib_formatter_parse_closingBraceAsFill_Buggy
2002 if (ctx.begin () == ctx.end () or *ctx.begin () == '}') {
2003 return ctx.begin ();
2004 }
2005#endif
2006 return fDelegate2_.parse (ctx);
2007 }
2008
2009 template <typename FmtContext>
2010 typename FmtContext::iterator format (Stroika::Foundation::Characters::String s, FmtContext& ctx) const
2011 {
2012 return fDelegate2_.format (s.As<std::wstring> (), ctx);
2013 }
2014 };
2015 template <>
2016 struct formatter<Stroika::Foundation::Characters::String, char> {
2017 qStroika_Foundation_Characters_FMT_PREFIX_::formatter<std::string_view, char> fDelegate2_;
2018
2019 template <typename ParseContext>
2020 constexpr typename ParseContext::iterator parse (ParseContext& ctx)
2021 {
2022#if qCompilerAndStdLib_formatter_parse_closingBraceAsFill_Buggy
2023 if (ctx.begin () == ctx.end () or *ctx.begin () == '}') {
2024 return ctx.begin ();
2025 }
2026#endif
2027 return fDelegate2_.parse (ctx);
2028 }
2029
2030 template <typename FmtContext>
2031 typename FmtContext::iterator format (Stroika::Foundation::Characters::String s, FmtContext& ctx) const
2032 {
2033 // in the SDK's narrow character set; a character it lacks is mapped as best it can be, rather than throwing
2034 return fDelegate2_.format (s.AsNarrowSDKString (Stroika::Foundation::Characters::eIgnoreErrors), ctx);
2035 }
2036 };
2037}
2038
2039/*
2040 ********************************************************************************
2041 ***************************** Implementation Details ***************************
2042 ********************************************************************************
2043 */
2044#include "String.inl"
2045
2046#endif /*_Stroika_Foundation_Characters_String_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
constexpr bool IsWhitespace() const noexcept
Is this blank, and a place text may break (between words, tokens, lines ...)? The C standard white-sp...
RegularExpression is a compiled regular expression which can be used to match on a String class.
virtual Character GetAt(size_t index) const noexcept=0
virtual PeekSpanData PeekData(optional< PeekSpanData::StorageCodePointType > preferred) const noexcept=0
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual bool Contains(Character c, CompareOptions co=eWithCase) const
Definition String.inl:699
nonvirtual size_t length() const noexcept
Definition String.inl:1049
nonvirtual String ToUpperCase() const
Definition String.cpp:1803
static String FromNarrowString(const char *from, const locale &l)
Definition String.inl:342
nonvirtual bool Matches(const RegularExpression &regEx) const
Definition String.cpp:1192
nonvirtual bool IsWhitespace() const
Definition String.cpp:1841
nonvirtual String NormalizeTextToNL() const
Definition String.cpp:1260
static String Join(const Iterable< String > &list, const String &separator=", "sv)
Definition String.cpp:1751
static String FromStringConstant(const CHAR_T(&cString)[SIZE])
Take the given argument data (constant span) - which must remain unchanged - constant - for the appli...
Definition String.inl:388
nonvirtual String ColValue(size_t i, const String &valueIfMissing={}) const
see Col(i) - but with default value of empty string
Definition String.inl:717
nonvirtual String NormalizeSpace(Character useSpaceCharacter=' ') const
Replace sequences of whitespace characters (space, tab, newline etc) with a single space (or argument...
Definition String.cpp:1288
nonvirtual Containers::Sequence< pair< size_t, size_t > > FindEach(const RegularExpression &regEx) const
Definition String.cpp:1025
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1053
nonvirtual bool operator==(const String &rhs) const
Definition String.inl:1113
nonvirtual String Repeat(unsigned int count) const
Definition String.cpp:1483
static String FromSDKString(const SDKChar *from)
Definition String.inl:449
nonvirtual String LimitLength(size_t maxLen, StringShorteningPreference keepPref=StringShorteningPreference::ePreferKeepLeft) const
return the first maxLen (or fewer if string shorter) characters of this string (adding ellipsis if tr...
Definition String.inl:747
nonvirtual String RemoveAll(Character c) const
Definition String.cpp:882
nonvirtual Containers::Sequence< RegularExpressionMatch > FindEachMatch(const RegularExpression &regEx) const
Definition String.cpp:1043
nonvirtual String RemoveFirstIf(Character c) const
Definition String.cpp:866
nonvirtual string AsNarrowSDKString() const
Definition String.inl:832
nonvirtual optional< String > Col(size_t i) const
Useful to replace 'awk print $3' - replace with Col(2) - zero based.
Definition String.cpp:1421
nonvirtual String InsertAt(Character c, size_t at) const
Definition String.inl:721
nonvirtual size_t rfind(Character c) const
Definition String.inl:1073
static String FromNarrowSDKString(const char *from)
Definition String.inl:472
nonvirtual string AsNarrowString(const locale &l) const
Definition String.cpp:1897
nonvirtual String Concatenate(T &&rhs) const
appends 'rhs' string to this string (without modifying this string) and returns the combined string
nonvirtual SDKString AsSDKString() const
Definition String.inl:804
nonvirtual size_t size() const noexcept
Definition String.inl:536
nonvirtual bool EndsWith(const Character &c, CompareOptions co=eWithCase) const
Definition String.cpp:1147
nonvirtual String ToLowerCase() const
Definition String.cpp:1765
nonvirtual String ReplaceAll(const RegularExpression &regEx, const String &with) const
Definition String.cpp:1214
static constexpr size_t npos
Definition String.h:1416
nonvirtual String Replace(size_t from, size_t to, const String &replacement) const
Definition String.cpp:1104
nonvirtual String SubString(SZ from) const
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
Definition String.cpp:1651
nonvirtual strong_ordering operator<=>(const String &rhs) const
Definition String.inl:1103
nonvirtual Character back() const
Definition String.inl:1077
nonvirtual bool StartsWith(const Character &c, CompareOptions co=eWithCase) const
Definition String.cpp:1118
nonvirtual String StripAll(bool(*removeCharIf)(Character)) const
Definition String.cpp:1723
nonvirtual String AssureEndsWith(const Character &c, CompareOptions co=eWithCase) const
Return *this if it ends with argument character, or append 'c' so that it ends with a 'c'.
Definition String.cpp:1182
nonvirtual span< CHAR_T > CopyTo(span< CHAR_T > s) const
nonvirtual PeekSpanData GetPeekSpanData() const
return the constant character data inside the string in the form of a case variant union of different...
nonvirtual String SafeSubString(SZ from) const
nonvirtual Containers::Sequence< String > AsLines() const
break the String into a series of lines;
Definition String.cpp:1365
nonvirtual String LTrim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
Definition String.cpp:1502
nonvirtual Character front() const
Definition String.inl:1084
nonvirtual Containers::Sequence< String > Grep(const String &fgrepArg) const
Breaks this string into Lines, with AsLines (), and applies the argument filter (as if with ....
Definition String.cpp:1400
nonvirtual Containers::Sequence< String > FindEachString(const RegularExpression &regEx) const
Definition String.cpp:1060
nonvirtual String Skip(size_t n) const
Return a substring of this string, starting at 'argument' n. If n > size(), return empty string.
Definition String.inl:606
nonvirtual optional< size_t > RFind(Character c) const noexcept
Definition String.cpp:1070
static span< const CHAR_TYPE > GetData(const PeekSpanData &pds, Memory::StackBuffer< CHAR_TYPE, STACK_BUFFER_SZ > *possiblyUsedBuffer)
return the constant character data inside the string (rep) in the form of a span, possibly quickly an...
Definition String.inl:965
nonvirtual Containers::Sequence< String > Tokenize() const
Definition String.cpp:1293
nonvirtual String RemoveAt(size_t charAt) const
Definition String.inl:610
nonvirtual optional< T > AsASCIIQuietly() const
static String FromLatin1(const CHAR_T *cString)
Definition String.inl:357
static optional< span< const CHAR_TYPE > > PeekData(const PeekSpanData &pds)
return the constant character data inside the string in the form of a span or nullopt if not availabl...
Definition String.inl:908
static String FromUTF8(span< CHAR_T > from)
Definition String.inl:422
nonvirtual String RTrim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
Definition String.cpp:1567
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
Definition String.inl:687
nonvirtual String substr(size_t from, size_t count=npos) const
Definition String.inl:1090
nonvirtual size_t find(Character c, size_t startAt=0) const
Definition String.inl:1065
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:234
Iterable(const Iterable &) noexcept=default
Iterable are safely copyable (by value). Since Iterable uses COW, this just copies the underlying poi...
returns true iff T == u8string, u16string, u32string, or wstring - which std::string types can be una...
Definition String.h:116
anything with a 'special .STRINGTYPE conversion' method to UNICODE string, such as filesystem::path
Definition String.h:124
IUNICODECanUnambiguouslyConvertFrom is any 'character representation type' where array of them unambi...
Definition Character.h:179
char ASCII
Stroika's string/character classes treat 'char' as being an ASCII character.
Definition Character.h:59
basic_string< SDKChar > SDKString
Definition SDKString.h:38
String operator+(LHS_T &&lhs, RHS_T &&rhs)
Definition String.inl:1292
const function< String(String, String, bool)> kDefaultStringCombiner
Definition String.inl:1317
wostream & operator<<(wostream &out, const String &s)
Definition String.cpp:2090
conditional_t< qStroika_Foundation_Characters_SDKUseswchar_t, wchar_t, char > SDKChar
Definition SDKChar.h:77
STL namespace.
very similar to ThreeWayComparer but returns true if less
Definition String.h:1884
Summary data for raw contents of rep - each rep will support at least one of these span forms.
Definition String.h:1287
StringCombiner is a simple function object used to combine two strings visually - used in Iterable<>:...
Definition String.h:1942
function object which serializes type T to a BLOB (or BLOB like) object