/
String.h
640 lines (553 loc) · 40.8 KB
/
String.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
#pragma once
#include <stdexcept>
#include <sstream>
#include <vector>
#include <string>
#include <ios>
/*
There are two "string" classes: ByteString and String. They have nearly
identical interfaces, except that one stores 8-bit octets (bytes) and
the other stores Unicode codepoints.
For ease of use, a String can be constructed from a string literal
(char const[]) however not char const *. Exercise care when working
with char arrays.
Both classes inherit from std::basic_string (std::string is an
instatiation of that), so all the familiar string interface is there
however some helper methods have been defined:
Substr(size_t start = 0, size_t count = npos)
Returns a substring starting at position <start> and counting
<count> symbols, or until end of string, whichever is earlier.
If count == npos, the entire remainder of the string is
included.
SubstrFromEnd(size_t rstart = 0, size_t rcount = npos)
Behaviourally equal to
reverse(reverse(str).Substr(rstart, rcount))
but is more efficient. Useful for taking suffixes of given
length or dropping a fixed number of symbols from the end.
Between(size_t begin, size_t end)
Returns a substring starting at <begin> and ending at <end>.
If end == npos, length of the string is used. If begin > end,
an empty string is returned.
Insert(size_t pos, String str)
Inserts the characters from <str> at position <pos> shifting
the rest of the string to the right.
Erase(size_t pos, size_t count)
Starting at position <pos> erases <count> characters to the
right or until the end of string. The rest of the string is
shifted left to fill the gap.
EraseBetween(size_t from, size_t to)
Starting at position <pos> erase until position <to> or end of
string, whichever is earlier. The rest of the string is shifted
left to fill the gap.
BeginsWith(String prefix)
EndsWith(String suffix)
Contains(String infix)
Contains(value_type infix)
Self-explanatory.
ByteString::FromUtf8(bool ignoreError = true)
Decodes UTF-8 byte sequences into Unicode codepoints.
If ignoreError is true, then invalid byte sequences are widened
as-is. Otherwise, a ConversionError is thrown.
ByteString::FromAscii()
Interprets byte values as Unicode codepoints.
String::ToUtf8()
Encodes Unicode codepoints into UTF-8 byte sequences.
String::ToAscii()
Narrows Unicode codepoints into bytes, possibly truncating
them (!).
To convert something into a string use ByteStringBuilder and
StringBuilder respectively. The two use operator<< much like
std::ostringstream. To convert a builder to a string use the Build()
method. Alternatively you could use the static ByteString::Build and
String::Build methods respectively. String::Build(x, y, z) is roughly
equivalent to:
StringBuilder tmp;
tmp << x << y << z;
return tmp.Build();
To control formatting of the input/output see "common/Format.h".
If you simply want to convert a string to a number you can use the
ToNumber<type>(bool ignoreError = false) method. It can optionally take
a formatting specifier as an argument:
str.ToNumber<unsigned>(Format::Hex(), true)
Otherwise to parse a string into components you can use splits. A Split
is a temporary object that "remembers" how a string is divided into a
"prefix", a "separator", and a "suffix". A split can also "fail", if,
for example the separator was not found in the string. A Split has the
following methods:
Before(bool includeSeparator = false)
Returns the "prefix", optionally with the "separator".
After(bool includeSeparator = false)
Returns the "suffix", optionally with the "separator".
A Split can also be converted to bool (used in the condition of an 'if'
or a 'while'), in which case it shows whether the split had succeeded
or failed. The idiomatic code goes like:
if(String::Split split = str.SplitBy(','))
// use split.Before() and split.After()
else
// str does not contain a ','
The following methods split a string:
SplitBy(String sep, size_t from = 0)
SplitBy(value_type sep, size_t from = 0)
Split on the first occurence of the <sep> separator since
position <from>. If no such separator is found the split fails
and "prefix" contains the whole string starting from <from>.
SplitByAny(String chars, size_t from = 0)
Split on the first occurence of any of the characters in
<chars>.
SplitByNot(String chars, size_t from = 0)
Split on the first occurence of any character that is *not* in
<chars>.
SplitFromEndBy(String sep, size_t from = npos)
SplitFromEndBy(value_type sep, size_t from = npos)
SplitFromEndByAny(String chars, size_t from = npos)
SplitFromEndByNot(String chars, size_t from = npos)
These do the same as the functions above except they try to
find the *last* occurence of the separator. If the split fails
it is the "suffix" that contains the whole string, and the
"prefix" is empty instead.
SplitNumber(number &ref, size_t pos = 0)
Attempt to read a number (with the type indicated by the type
of the reference) from position <pos>. In case of success store
the parsed number in the reference and return a split at the
end of the parse. The separator in this case is empty. If the
parse fails, the "prefix" is empty.
SplitNumber(number &ref, format, size_t pos = 0)
Parse the number according to the provided formatting
specifier.
It is recommented to use ByteString::value_type and String::value_type
instead of char and char32_t respectively.
The reason we do not use std::wstring is that on Windows, wchar_t is a
16-bit type, which forces the usage of a UTF-16 (UCS-2) encoding to
store the higher parts of the Unicode. The std::wstring implementation
does not care to handle the UTF-16 encoding. Considering characters
still occupy a variable amount of space (1 or 2 wchar_t's), finding the
"n'th character" or "index of character at offset n" becomes a problem.
(Event if that were not a problem a better solution would be to use the
more space-efficient UTF-8). Therefore the String class is derived from
std::basic_string<char32_t>, where char32_t is a type that is
guaranteed to contain at least 32 bits. The drawback is that we
basically lose std::stringstream (std::basic_stringstream) support and
have to implement our own (See "common/String.cpp").
*/
class ByteStringBuilder;
class String;
class StringBuilder;
namespace Format
{
const std::ios_base::fmtflags EmptyFmtFlags = std::ios_base::dec & ~std::ios_base::dec;
template<typename T, std::ios_base::fmtflags set, std::ios_base::fmtflags reset> struct FlagsOverride;
}
template<typename T> class SplitBase
{
T const &parent;
size_t posFrom;
size_t posBefore;
size_t posAfter;
bool reverse;
inline SplitBase(T const &_parent, size_t _posFrom, size_t _posBefore, size_t offset, bool _reverse):
parent(_parent),
posFrom(_posFrom),
posBefore(_posBefore),
posAfter(_posBefore == T::npos ? T::npos : _posBefore + offset),
reverse(_reverse)
{}
public:
inline T Before(bool includeSeparator = false) const
{
if(posBefore == T::npos)
return reverse ? T() : parent.Substr(posFrom);
return parent.Between(reverse ? 0 : posFrom, includeSeparator ? posAfter : posBefore);
}
inline T After(bool includeSeparator = false) const
{
if(posBefore == T::npos)
return reverse ? parent.Substr(0, posFrom) : T();
return parent.Between(includeSeparator ? posBefore : posAfter, reverse ? posFrom : T::npos);
}
inline size_t PositionFrom() const { return posFrom; }
inline size_t PositionBefore() const { return posBefore; }
inline size_t PositionAfter() const { return posAfter; }
inline operator bool() const { return posBefore != T::npos; }
friend T;
};
class ByteString : public std::basic_string<char>
{
using super = std::basic_string<char>;
public:
inline ByteString(): super() {}
inline ByteString(value_type ch): super(1, ch) {}
inline ByteString(size_type count, value_type ch): super(count, ch) {}
inline ByteString(value_type const *ch, size_type count): super(ch, count) {}
inline ByteString(value_type const *ch): super(ch) {}
template<class It> inline ByteString(It first, It last): super(first, last) {}
inline ByteString(super const &other): super(other) {}
inline ByteString(super &&other): super(std::move(other)) {}
inline ByteString(ByteString const &other): super(other) {}
inline ByteString(ByteString &&other): super(std::move(other)) {}
inline ByteString &operator=(ByteString const &other) { super::operator=(other); return *this; }
inline ByteString &operator=(ByteString &&other) { super::operator=(other); return *this; }
inline ByteString &operator+=(ByteString const &other) { super::operator+=(other); return *this; }
inline ByteString &operator+=(ByteString &&other) { super::operator+=(std::move(other)); return *this; }
inline ByteString Substr(size_t pos = 0, size_t count = npos) const { return super::substr(pos, count); }
inline ByteString SubstrFromEnd(size_t rpos = 0, size_t rcount = npos) const { return super::substr(rcount == npos || rcount > rpos ? 0 : rpos - rcount, size() - rpos); }
inline ByteString Between(size_t from, size_t to) const { return to == npos ? super::substr(from) : from >= to ? ByteString() : super::substr(from, to - from); }
inline bool Contains(value_type ch) const { return super::find(ch) != npos; }
inline bool Contains(ByteString const &other) const { return super::find(other) != npos; }
inline bool BeginsWith(ByteString const &other) const { return !super::compare(0, other.size(), other); }
inline bool EndsWith(ByteString const &other) const { return !super::compare(size() - other.size(), other.size(), other); }
using Split = SplitBase<ByteString>;
inline Split SplitBy(value_type ch, size_t pos = 0) const { return Split(*this, pos, super::find(ch, pos), 1, false); }
inline Split SplitBy(ByteString const &str, size_t pos = 0) const { return Split(*this, pos, super::find(str, pos), str.size(), false); }
inline Split SplitByAny(ByteString const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_of(str, pos), 1, false); }
inline Split SplitByNot(ByteString const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_not_of(str, pos), 1, false); }
inline Split SplitFromEndBy(value_type ch, size_t pos = npos) const { return Split(*this, pos, super::rfind(ch, pos), 1, true); }
inline Split SplitFromEndBy(ByteString const &str, size_t pos = npos) const { return Split(*this, pos, super::find(str, pos), str.size(), true); }
inline Split SplitFromEndByAny(ByteString const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_of(str, pos), 1, true); }
inline Split SplitFromEndByNot(ByteString const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_not_of(str, pos), 1, true); }
private:
Split SplitSigned(long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const;
Split SplitUnsigned(unsigned long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const;
Split SplitFloat(double &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const;
public:
template<typename T> inline Split SplitSigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const
{
long long int value = 0;
Split split = SplitSigned(value, pos, set, reset);
ref = value;
return split;
}
template<typename T> inline Split SplitUnsigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const
{
unsigned long long int value = 0;
Split split = SplitUnsigned(value, pos, set, reset);
ref = value;
return split;
}
template<typename T> inline Split SplitFloat(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const
{
double value = 0;
Split split = SplitFloat(value, pos, set, reset);
ref = value;
return split;
}
inline Split SplitNumber(short int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(long long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned short int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned long long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(float &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(double &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(short int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(long long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned short int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned long long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(float &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(double &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); }
template<typename T> T ToNumber(bool noThrow = false) const
{
T value = T();
Split split = SplitNumber(value);
if(split.PositionBefore() != size())
return noThrow ? T() : throw std::runtime_error("Not a number");
return value;
}
template<typename T, std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline T ToNumber(Format::FlagsOverride<void, set, reset> fmt, bool noThrow = false) const
{
T value = T();
Split split = SplitNumber(value, fmt);
if(split.PositionBefore() != size())
return noThrow ? T() : throw std::runtime_error("Not a number");
return value;
}
std::vector<ByteString> PartitionBy(value_type ch, bool includeEmpty = false) const;
std::vector<ByteString> PartitionBy(ByteString const &str, bool includeEmpty = false) const;
std::vector<ByteString> PartitionByAny(ByteString const &str, bool includeEmpty = false) const;
ByteString &Substitute(ByteString const &needle, ByteString const &replacement);
inline ByteString &Insert(size_t pos, ByteString const &str) { super::insert(pos, str); return *this; }
inline ByteString &Erase(size_t pos, size_t count) { super::erase(pos, count); return *this; }
inline ByteString &EraseBetween(size_t from, size_t to) { if(from < to) super::erase(from, to - from); return *this; }
String FromUtf8(bool ignoreError = true) const;
inline String FromAscii() const;
template<typename... Ts> static ByteString Build(Ts&&... args);
};
inline ByteString operator+(ByteString const &lhs, ByteString const &rhs) { return static_cast<std::basic_string<char> const &>(lhs) + static_cast<std::basic_string<char> const &>(rhs); }
inline ByteString operator+(ByteString const &lhs, ByteString &&rhs) { return static_cast<std::basic_string<char> const &>(lhs) + static_cast<std::basic_string<char> &&>(rhs); }
inline ByteString operator+(ByteString &&lhs, ByteString const &rhs) { return static_cast<std::basic_string<char> &&>(lhs) + static_cast<std::basic_string<char> const &>(rhs); }
inline ByteString operator+(ByteString &&lhs, ByteString &&rhs) { return static_cast<std::basic_string<char> &&>(lhs) + static_cast<std::basic_string<char> &&>(rhs); }
inline ByteString operator+(ByteString const &lhs, std::basic_string<char> const &rhs) { return static_cast<std::basic_string<char> const &>(lhs) + rhs; }
inline ByteString operator+(ByteString const &lhs, std::basic_string<char> &&rhs) { return static_cast<std::basic_string<char> const &>(lhs) + std::move(rhs); }
inline ByteString operator+(ByteString &&lhs, std::basic_string<char> const &rhs) { return static_cast<std::basic_string<char> &&>(lhs) + rhs; }
inline ByteString operator+(ByteString &&lhs, std::basic_string<char> &&rhs) { return static_cast<std::basic_string<char> &&>(lhs) + std::move(rhs); }
inline ByteString operator+(ByteString const &lhs, ByteString::value_type rhs) { return static_cast<std::basic_string<char> const &>(lhs) + rhs; }
inline ByteString operator+(ByteString &&lhs, ByteString::value_type rhs) { return static_cast<std::basic_string<char> &&>(lhs) + rhs; }
inline ByteString operator+(ByteString const &lhs, ByteString::value_type const *rhs) { return static_cast<std::basic_string<char> const &>(lhs) + rhs; }
inline ByteString operator+(ByteString &&lhs, ByteString::value_type const *rhs) { return static_cast<std::basic_string<char> &&>(lhs) + rhs; }
inline ByteString operator+(std::basic_string<char> const &lhs, ByteString const &rhs) { return lhs + static_cast<std::basic_string<char> const &>(rhs); }
inline ByteString operator+(std::basic_string<char> const &lhs, ByteString &&rhs) { return lhs + static_cast<std::basic_string<char> &&>(rhs); }
inline ByteString operator+(std::basic_string<char> &&lhs, ByteString const &rhs) { return std::move(lhs) + static_cast<std::basic_string<char> const &>(rhs); }
inline ByteString operator+(std::basic_string<char> &&lhs, ByteString &&rhs) { return std::move(lhs) + static_cast<std::basic_string<char> &&>(rhs); }
inline ByteString operator+(ByteString::value_type lhs, ByteString const &rhs) { return lhs + static_cast<std::basic_string<char> const &>(rhs); }
inline ByteString operator+(ByteString::value_type lhs, ByteString &&rhs) { return lhs + static_cast<std::basic_string<char> &&>(rhs); }
inline ByteString operator+(ByteString::value_type const *lhs, ByteString const &rhs) { return lhs + static_cast<std::basic_string<char> const &>(rhs); }
inline ByteString operator+(ByteString::value_type const *lhs, ByteString &&rhs) { return lhs + static_cast<std::basic_string<char> &&>(rhs); }
inline bool operator==(ByteString const &lhs, ByteString const &rhs) { return static_cast<std::basic_string<char> const &>(lhs) == static_cast<std::basic_string<char> const &>(rhs); }
inline bool operator==(ByteString const &lhs, std::basic_string<char> const &rhs) { return static_cast<std::basic_string<char> const &>(lhs) == rhs; }
inline bool operator==(ByteString const &lhs, ByteString::value_type const *rhs) { return static_cast<std::basic_string<char> const &>(lhs) == rhs; }
inline bool operator==(std::basic_string<char> const &lhs, ByteString const &rhs) { return lhs == static_cast<std::basic_string<char> const &>(rhs); }
inline bool operator==(ByteString::value_type const *lhs, ByteString const &rhs) { return lhs == static_cast<std::basic_string<char> const &>(rhs); }
inline bool operator!=(ByteString const &lhs, ByteString const &rhs) { return static_cast<std::basic_string<char> const &>(lhs) != static_cast<std::basic_string<char> const &>(rhs); }
inline bool operator!=(ByteString const &lhs, std::basic_string<char> const &rhs) { return static_cast<std::basic_string<char> const &>(lhs) != rhs; }
inline bool operator!=(ByteString const &lhs, ByteString::value_type const *rhs) { return static_cast<std::basic_string<char> const &>(lhs) != rhs; }
inline bool operator!=(std::basic_string<char> const &lhs, ByteString const &rhs) { return lhs != static_cast<std::basic_string<char> const &>(rhs); }
inline bool operator!=(ByteString::value_type const *lhs, ByteString const &rhs) { return lhs != static_cast<std::basic_string<char> const &>(rhs); }
class String : public std::basic_string<char32_t>
{
using super = std::basic_string<char32_t>;
public:
inline String(): super() {}
inline String(value_type ch): super(1, ch) {}
inline String(size_type count, value_type ch): super(count, ch) {}
inline String(value_type const *ch, size_type count): super(ch, count) {}
inline String(value_type const *ch): super(ch) {}
template<class It> inline String(It first, It last): super(first, last) {}
inline String(super const &other): super(other) {}
inline String(super &&other): super(std::move(other)) {}
inline String(String const &other): super(other) {}
inline String(String &&other): super(std::move(other)) {}
template<size_t N> inline String(ByteString::value_type const (&ch)[N]): super(ByteString(ch, N - 1).FromAscii()) {}
inline String &operator=(String const &other) { super::operator=(other); return *this; }
inline String &operator=(String &&other) { super::operator=(other); return *this; }
inline String &operator+=(String const &other) { super::operator+=(other); return *this; }
inline String &operator+=(String &&other) { super::operator+=(std::move(other)); return *this; }
inline String Substr(size_t pos = 0, size_t count = npos) const { return super::substr(pos, count); }
inline String SubstrFromEnd(size_t rpos = 0, size_t rcount = npos) const { return super::substr(rcount == npos || rcount > rpos ? 0 : rpos - rcount, size() - rpos); }
inline String Between(size_t from, size_t to) const { return to == npos ? super::substr(from) : from >= to ? String() : super::substr(from, to - from); }
inline bool Contains(value_type ch) const { return super::find(ch) != npos; }
inline bool Contains(String const &other) const { return super::find(other) != npos; }
inline bool BeginsWith(String const &other) const { return !super::compare(0, other.size(), other); }
inline bool EndsWith(String const &other) const { return !super::compare(size() - other.size(), other.size(), other); }
using Split = SplitBase<String>;
inline Split SplitBy(value_type ch, size_t pos = 0) const { return Split(*this, pos, super::find(ch, pos), 1, false); }
inline Split SplitBy(String const &str, size_t pos = 0) const { return Split(*this, pos, super::find(str, pos), str.size(), false); }
inline Split SplitByAny(String const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_of(str, pos), 1, false); }
inline Split SplitByNot(String const &str, size_t pos = 0) const { return Split(*this, pos, super::find_first_not_of(str, pos), 1, false); }
inline Split SplitFromEndBy(value_type ch, size_t pos = npos) const { return Split(*this, pos, super::rfind(ch, pos), 1, true); }
inline Split SplitFromEndBy(String const &str, size_t pos = npos) const { return Split(*this, pos, super::find(str, pos), str.size(), true); }
inline Split SplitFromEndByAny(String const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_of(str, pos), 1, true); }
inline Split SplitFromEndByNot(String const &str, size_t pos = npos) const { return Split(*this, pos, super::find_last_not_of(str, pos), 1, true); }
private:
Split SplitSigned(long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const;
Split SplitUnsigned(unsigned long long int &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const;
Split SplitFloat(double &, size_t, std::ios_base::fmtflags, std::ios_base::fmtflags) const;
public:
template<typename T> inline Split SplitSigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const
{
long long int value = 0;
Split split = SplitSigned(value, pos, set, reset);
ref = value;
return split;
}
template<typename T> inline Split SplitUnsigned(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const
{
unsigned long long int value = 0;
Split split = SplitUnsigned(value, pos, set, reset);
ref = value;
return split;
}
template<typename T> inline Split SplitFloat(T &ref, size_t pos, std::ios_base::fmtflags set, std::ios_base::fmtflags reset) const
{
double value = 0;
Split split = SplitFloat(value, pos, set, reset);
ref = value;
return split;
}
inline Split SplitNumber(short int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(long long int &ref, size_t pos = 0) const { return SplitSigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned short int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(unsigned long long int &ref, size_t pos = 0) const { return SplitUnsigned(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(float &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
inline Split SplitNumber(double &ref, size_t pos = 0) const { return SplitFloat(ref, pos, std::ios_base::fmtflags(), std::ios_base::fmtflags()); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(short int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(long long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitSigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned short int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(unsigned long long int &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitUnsigned(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(float &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); }
template<std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline Split SplitNumber(double &ref, Format::FlagsOverride<void, set, reset>, size_t pos = 0) const { return SplitFloat(ref, pos, set, reset); }
template<typename T> T ToNumber(bool noThrow = false) const
{
T value = T();
Split split = SplitNumber(value);
if(split.PositionBefore() != size())
return noThrow ? T() : throw std::runtime_error("Not a number");
return value;
}
template<typename T, std::ios_base::fmtflags set, std::ios_base::fmtflags reset> inline T ToNumber(Format::FlagsOverride<void, set, reset> fmt, bool noThrow = false) const
{
T value = T();
Split split = SplitNumber(value, fmt);
if(split.PositionBefore() != size())
return noThrow ? T() : throw std::runtime_error("Not a number");
return value;
}
std::vector<String> PartitionBy(value_type ch, bool includeEmpty = false) const;
std::vector<String> PartitionBy(String const &str, bool includeEmpty = false) const;
std::vector<String> PartitionByAny(String const &str, bool includeEmpty = false) const;
String &Substitute(String const &needle, String const &replacement);
inline String &Insert(size_t pos, String const &str) { super::insert(pos, str); return *this; }
inline String &Erase(size_t pos, size_t count) { super::erase(pos, count); return *this; }
inline String &EraseBetween(size_t from, size_t to) { if(from < to) super::erase(from, to - from); return *this; }
ByteString ToUtf8() const;
ByteString ToAscii() const;
template<typename... Ts> static String Build(Ts&&... args);
};
inline String operator+(String const &lhs, String const &rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) + static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String operator+(String const &lhs, String &&rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) + static_cast<std::basic_string<char32_t> &&>(rhs); }
inline String operator+(String &&lhs, String const &rhs) { return static_cast<std::basic_string<char32_t> &&>(lhs) + static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String operator+(String &&lhs, String &&rhs) { return static_cast<std::basic_string<char32_t> &&>(lhs) + static_cast<std::basic_string<char32_t> &&>(rhs); }
inline String operator+(String const &lhs, std::basic_string<char32_t> const &rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) + rhs; }
inline String operator+(String const &lhs, std::basic_string<char32_t> &&rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) + std::move(rhs); }
inline String operator+(String &&lhs, std::basic_string<char32_t> const &rhs) { return static_cast<std::basic_string<char32_t> &&>(lhs) + rhs; }
inline String operator+(String &&lhs, std::basic_string<char32_t> &&rhs) { return static_cast<std::basic_string<char32_t> &&>(lhs) + std::move(rhs); }
inline String operator+(String const &lhs, String::value_type rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) + rhs; }
inline String operator+(String &&lhs, String::value_type rhs) { return static_cast<std::basic_string<char32_t> &&>(lhs) + rhs; }
inline String operator+(String const &lhs, String::value_type const *rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) + rhs; }
inline String operator+(String &&lhs, String::value_type const *rhs) { return static_cast<std::basic_string<char32_t> &&>(lhs) + rhs; }
inline String operator+(std::basic_string<char32_t> const &lhs, String const &rhs) { return lhs + static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String operator+(std::basic_string<char32_t> const &lhs, String &&rhs) { return lhs + static_cast<std::basic_string<char32_t> &&>(rhs); }
inline String operator+(std::basic_string<char32_t> &&lhs, String const &rhs) { return std::move(lhs) + static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String operator+(std::basic_string<char32_t> &&lhs, String &&rhs) { return std::move(lhs) + static_cast<std::basic_string<char32_t> &&>(rhs); }
inline String operator+(String::value_type lhs, String const &rhs) { return lhs + static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String operator+(String::value_type lhs, String &&rhs) { return lhs + static_cast<std::basic_string<char32_t> &&>(rhs); }
inline String operator+(String::value_type const *lhs, String const &rhs) { return lhs + static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String operator+(String::value_type const *lhs, String &&rhs) { return lhs + static_cast<std::basic_string<char32_t> &&>(rhs); }
template<size_t N> inline String operator+(String const &lhs, ByteString::value_type const (&rhs)[N]) { return static_cast<std::basic_string<char32_t> const &>(lhs) + ByteString(rhs).FromAscii(); }
template<size_t N> inline String operator+(String &&lhs, ByteString::value_type const (&rhs)[N]) { return static_cast<std::basic_string<char32_t> &&>(lhs) + ByteString(rhs).FromAscii(); }
template<size_t N> inline String operator+(ByteString::value_type const (&lhs)[N], String const &rhs) { return ByteString(lhs).FromAscii() + static_cast<std::basic_string<char32_t> const &>(rhs); }
template<size_t N> inline String operator+(ByteString::value_type const (&lhs)[N], String &&rhs) { return ByteString(lhs).FromAscii() + static_cast<std::basic_string<char32_t> &&>(rhs); }
inline bool operator==(String const &lhs, String const &rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) == static_cast<std::basic_string<char32_t> const &>(rhs); }
inline bool operator==(String const &lhs, std::basic_string<char32_t> const &rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) == rhs; }
inline bool operator==(String const &lhs, String::value_type const *rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) == rhs; }
inline bool operator==(std::basic_string<char32_t> const &lhs, String const &rhs) { return lhs == static_cast<std::basic_string<char32_t> const &>(rhs); }
inline bool operator==(String::value_type const *lhs, String const &rhs) { return lhs == static_cast<std::basic_string<char32_t> const &>(rhs); }
template<size_t N> inline bool operator==(String const &lhs, ByteString::value_type const (&rhs)[N]) { return static_cast<std::basic_string<char32_t> const &>(lhs) == ByteString(rhs).FromAscii(); }
template<size_t N> inline bool operator==(ByteString::value_type const (&lhs)[N], String const &rhs) { return ByteString(lhs).FromAscii() == static_cast<std::basic_string<char32_t> const &>(rhs); }
inline bool operator!=(String const &lhs, String const &rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) != static_cast<std::basic_string<char32_t> const &>(rhs); }
inline bool operator!=(String const &lhs, std::basic_string<char32_t> const &rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) != rhs; }
inline bool operator!=(String const &lhs, String::value_type const *rhs) { return static_cast<std::basic_string<char32_t> const &>(lhs) != rhs; }
inline bool operator!=(std::basic_string<char32_t> const &lhs, String const &rhs) { return lhs != static_cast<std::basic_string<char32_t> const &>(rhs); }
inline bool operator!=(String::value_type const *lhs, String const &rhs) { return lhs != static_cast<std::basic_string<char32_t> const &>(rhs); }
template<size_t N> inline bool operator!=(String const &lhs, ByteString::value_type const (&rhs)[N]) { return static_cast<std::basic_string<char32_t> const &>(lhs) != ByteString(rhs).FromAscii(); }
template<size_t N> inline bool operator!=(ByteString::value_type const (&lhs)[N], String const &rhs) { return ByteString(lhs).FromAscii() != static_cast<std::basic_string<char32_t> const &>(rhs); }
inline String ByteString::FromAscii() const
{
String destination = String(size(), String::value_type());
for(size_t i = 0; i < size(); i++)
destination[i] = String::value_type(std::make_unsigned<ByteString::value_type>::type(operator[](i)));
return destination;
}
inline ByteString String::ToAscii() const
{
ByteString destination = ByteString(size(), ByteString::value_type());
for(size_t i = 0; i < size(); i++)
destination[i] = ByteString::value_type(operator[](i));
return destination;
}
class ConversionError : public std::runtime_error
{
static ByteString formatError(ByteString::value_type const *at, ByteString::value_type const *upto);
public:
inline ConversionError(ByteString::value_type const *at, ByteString::value_type const *upto): std::runtime_error(formatError(at, upto)) {}
inline ConversionError(bool to): std::runtime_error(to ? "Could not convert to UTF-8" : "Could not convert from UTF-8") {}
};
class ByteStringBuilder
{
std::vector<ByteString::value_type> buffer;
public:
std::ios_base::fmtflags flags;
ByteString::value_type fill;
size_t width, precision;
inline ByteStringBuilder(): flags(std::ios_base::skipws | std::ios_base::dec), fill(' '), width(0), precision(6) {}
void AddChars(ByteString::value_type const *, size_t);
size_t Size() const { return buffer.size(); }
ByteString Build() const;
template<typename T> ByteStringBuilder &operator<<(T) &&= delete;
template<typename T, typename... Ts> ByteStringBuilder &Add(T &&arg, Ts&&... args)
{
return (*this << std::forward<T>(arg)).Add(std::forward<Ts>(args)...);
}
ByteStringBuilder &Add() { return *this; }
};
ByteStringBuilder &operator<<(ByteStringBuilder &, short int);
ByteStringBuilder &operator<<(ByteStringBuilder &, int);
ByteStringBuilder &operator<<(ByteStringBuilder &, long int);
ByteStringBuilder &operator<<(ByteStringBuilder &, long long int);
ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned short int);
ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned int);
ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned long int);
ByteStringBuilder &operator<<(ByteStringBuilder &, unsigned long long int);
ByteStringBuilder &operator<<(ByteStringBuilder &, ByteString::value_type);
ByteStringBuilder &operator<<(ByteStringBuilder &, ByteString::value_type const *);
ByteStringBuilder &operator<<(ByteStringBuilder &, ByteString const &);
ByteStringBuilder &operator<<(ByteStringBuilder &, float);
ByteStringBuilder &operator<<(ByteStringBuilder &, double);
template<typename... Ts> ByteString ByteString::Build(Ts&&... args)
{
ByteStringBuilder b;
b.Add(std::forward<Ts>(args)...);
return b.Build();
}
class StringBuilder
{
std::vector<String::value_type> buffer;
public:
std::ios_base::fmtflags flags;
String::value_type fill;
size_t width, precision;
inline StringBuilder(): flags(std::ios_base::skipws | std::ios_base::dec), fill(' '), width(0), precision(6) {}
void AddChars(String::value_type const *, size_t);
size_t Size() const { return buffer.size(); }
String Build() const;
template<typename T> StringBuilder &operator<<(T) = delete;
template<typename T, typename... Ts> StringBuilder &Add(T &&arg, Ts&&... args)
{
return (*this << std::forward<T>(arg)).Add(std::forward<Ts>(args)...);
}
StringBuilder &Add() { return *this; }
};
StringBuilder &operator<<(StringBuilder &, short int);
StringBuilder &operator<<(StringBuilder &, int);
StringBuilder &operator<<(StringBuilder &, long int);
StringBuilder &operator<<(StringBuilder &, long long int);
StringBuilder &operator<<(StringBuilder &, unsigned short int);
StringBuilder &operator<<(StringBuilder &, unsigned int);
StringBuilder &operator<<(StringBuilder &, unsigned long int);
StringBuilder &operator<<(StringBuilder &, unsigned long long int);
StringBuilder &operator<<(StringBuilder &, ByteString::value_type);
StringBuilder &operator<<(StringBuilder &, String::value_type);
StringBuilder &operator<<(StringBuilder &, String::value_type const *);
StringBuilder &operator<<(StringBuilder &, String const &);
StringBuilder &operator<<(StringBuilder &, float);
StringBuilder &operator<<(StringBuilder &, double);
template<size_t N> StringBuilder &operator<<(StringBuilder &b, ByteString::value_type const (&data)[N]) { return b << ByteString(data).FromUtf8(); }
template<typename... Ts> String String::Build(Ts&&... args)
{
StringBuilder b;
b.Add(std::forward<Ts>(args)...);
return b.Build();
}
#include "common/Format.h"