This repository has been archived on 2025-03-20. You can view files and clone it, but cannot push or open issues or pull requests.
The-Powder-Toy/src/common/String.h

687 lines
42 KiB
C++

#pragma once
#include <stdexcept>
#include <sstream>
#include <vector>
#include <string>
#include <limits>
#include <locale>
#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.
ToLower()
ToUpper()
Lowercases/Uppercases characters in the string. Only works on
characters in the ASCII range.
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::rfind(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; }
inline ByteString ToLower() const
{
std::locale const &loc = std::locale::classic();
ByteString value(*this);
for(value_type &ch : value)
ch = std::tolower(ch, loc);
return value;
}
inline ByteString ToUpper() const
{
std::locale const &loc = std::locale::classic();
ByteString value(*this);
for(value_type &ch : value)
ch = std::toupper(ch, loc);
return value;
}
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::rfind(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; }
inline String ToLower() const
{
std::locale const &loc = std::locale::classic();
String value(*this);
for(value_type &ch : value)
if(ch <= static_cast<value_type>(std::numeric_limits<ByteString::value_type>::max()))
ch = std::tolower(static_cast<ByteString::value_type>(ch), loc);
return value;
}
inline String ToUpper() const
{
std::locale const &loc = std::locale::classic();
String value(*this);
for(value_type &ch : value)
if(ch <= static_cast<value_type>(std::numeric_limits<ByteString::value_type>::max()))
ch = std::toupper(static_cast<ByteString::value_type>(ch), loc);
return value;
}
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> inline ByteStringBuilder &Add(T &&arg, Ts&&... args)
{
return (*this << std::forward<T>(arg)).Add(std::forward<Ts>(args)...);
}
inline 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> inline StringBuilder &Add(T &&arg, Ts&&... args)
{
return (*this << std::forward<T>(arg)).Add(std::forward<Ts>(args)...);
}
inline 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"
#include "common/Internationalization.h"