12#include <initializer_list>
23 struct String::StringImpl {
25 StringImpl() =
default;
26 StringImpl(
const char* str) : s(str) {}
27 StringImpl(
const std::string& str) : s(str) {}
28 StringImpl(std::string&& str) : s(std::move(str)) {}
35 inline String ToStringHelper(
const T& val) {
36 using DecayedT = std::decay_t<T>;
37 if constexpr (std::is_same_v<DecayedT, String>) {
40 else if constexpr (std::is_same_v<DecayedT, std::string>) {
41 return String(val.c_str());
43 else if constexpr (std::is_same_v<DecayedT, std::nullptr_t>) {
46 else if constexpr (std::is_same_v<DecayedT, bool>) {
47 return val ?
"True" :
"False";
50 std::ostringstream ss;
52 return String(ss.str().c_str());
67 static void AppendExplicitArg(
const std::string& sFmt,
size_t i,
size_t end,
const String* pArgs,
int iArgCount, std::string& sRes) {
69 std::string num = sFmt.substr(i + 1, end - i - 1);
71 int argIdx = std::stoi(num, &idx);
72 if (idx != num.length() || argIdx < 0 || argIdx >= iArgCount)
throw FormatException(
"Index out of bounds");
78 static void FormatOpenBrace(
const std::string& sFmt,
size_t& i,
int& iAutoIndex,
const String* pArgs,
int iArgCount, std::string& sRes) {
80 if (i + 1 < sFmt.length() && sFmt[i+1] ==
'{') {
82 }
else if (i + 1 < sFmt.length() && sFmt[i+1] ==
'}') {
84 if (iAutoIndex >= iArgCount)
throw FormatException(
"Index out of bounds");
89 size_t end = sFmt.find(
'}', i + 1);
98 if (i + 1 < sFmt.length() && sFmt[i+1] ==
'}') {
109 if (iArgCount == 0 || !pArgs)
return String(pFormat);
112 std::string sFmt = pFormat;
115 for (
size_t i = 0; i < sFmt.length(); ++i) {
116 if (sFmt[i] ==
'{') {
118 }
else if (sFmt[i] ==
'}') {
126 return String(sRes.c_str());
129 void* String::operator
new(
size_t size) {
130 return ::operator
new(size);
133 void String::operator
delete(
void* p) {
134 ::operator
delete(p);
137 void* String::operator
new[](
size_t size) {
138 return ::operator
new[](size);
141 void String::operator
delete[](
void* p) {
142 ::operator
delete[](p);
148 sNewStr.m_pImpl->s.append(pStr);
155 sNewStr.m_pImpl->s.push_back(ch);
166 m_pImpl->s.append(pStr);
172 m_pImpl->s.push_back(ch);
177 String newStr(pStr ? pStr :
"");
178 return newStr + sStr;
182 char buf[2] = { ch, 0 };
184 return newStr + sStr;
190 m_pImpl =
new StringImpl();
198 m_pImpl->s = sStr.m_pImpl->s;
206 m_pImpl->s = std::move(sStr.m_pImpl->s);
212 m_pImpl =
new StringImpl();
231 return m_pImpl->s.c_str();
239 unsigned int hash = 2166136261u;
241 hash ^= (
unsigned char)(*p);
248 return static_cast<int>(m_pImpl->s.length());
262 std::transform(res.begin(), res.end(), res.begin(), [](
char ch) {
263 return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
270 return sStr1.m_pImpl->s.compare(iIndex1, iLength, sStr2.m_pImpl->s, iIndex2, iLength);
272 auto s1 =
ToLowerString(sStr1.m_pImpl->s.substr(iIndex1, iLength));
273 auto s2 =
ToLowerString(sStr2.m_pImpl->s.substr(iIndex2, iLength));
274 return s1.compare(s2);
281 const std::initializer_list<String> sStrs)
const {
283 for (
auto sStr : sStrs) {
284 sNewStr.m_pImpl->s.append(sStr.GetRawString());
289 const String& sStr)
const {
293 return m_pImpl->s.find(ch) != std::string::npos;
296 return m_pImpl->s.find(sStr.m_pImpl->s) != std::string::npos;
300 int iDestinationIndex,
int iDestArraySize,
302 if (
nullptr == pDestination)
305 if (iSourceIndex < 0 || iSourceIndex >= iLen)
309 "Source array size is smaller than iCount");
310 if (iCount > iDestArraySize)
312 "Destination array is smaller than iCount");
313 m_pImpl->s.copy(pDestination + iDestinationIndex, iCount, iSourceIndex);
316 auto iLen = m_pImpl->s.length();
317 if (iLen == 0)
return false;
319 return std::tolower(
static_cast<unsigned char>(m_pImpl->s [iLen - 1])) == std::tolower(
static_cast<unsigned char>(ch));
321 return m_pImpl->s [iLen - 1] == ch;
324 bool bIgnoreCase)
const {
327 if (iSuffixLen > iLen)
return false;
330 return Compare(*
this, iLen - iSuffixLen, sSuffix, 0, iSuffixLen,
true) == 0;
332 return m_pImpl->s.compare(
static_cast<size_t>(iLen - iSuffixLen),
static_cast<size_t>(iSuffixLen), sSuffix.m_pImpl->s) == 0;
336 return sStr1 == sStr2;
339 return *
this == sStr;
342 return IndexOf(sSubstring, 0,
false);
345 bool bIgnoreCase)
const {
346 return IndexOf(sSubstring, 0, bIgnoreCase);
349 int iStartIndex,
bool bIgnoreCase)
const {
350 if (iStartIndex < 0 || iStartIndex >
GetLength())
353 if (sSubstring.
IsEmpty())
return iStartIndex;
356 auto pos = m_pImpl->s.find(sSubstring.m_pImpl->s, iStartIndex);
357 return (pos == std::string::npos) ? -1 : (int)pos;
361 auto it = std::search(
362 m_pImpl->s.begin() + iStartIndex, m_pImpl->s.end(),
363 sSubstring.m_pImpl->s.begin(), sSubstring.m_pImpl->s.end(),
364 [](
char c1,
char c2) { return std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2)); }
367 return (it == m_pImpl->s.end()) ? -1 : (int)std::distance(m_pImpl->s.begin(), it);
370 std::initializer_list<char> chChars) {
371 if (iStartIndex < 0 || iStartIndex >
GetLength())
374 auto pos = m_pImpl->s.find_first_of(std::string(chChars.begin(), chChars.end()), iStartIndex);
375 return (pos == std::string::npos) ? -1 : (int)pos;
390 int iIndex,
const String& sStr) {
392 if (iIndex < 0 || iIndex > iLen)
399 return m_pImpl->s.empty();
403 char chSeparator, std::initializer_list<String> sStrings) {
404 return Join(chSeparator, sStrings, 0, (
int)sStrings.size());
407 char chSeparator, std::initializer_list<String> sStrings,
408 int iStartIndex,
int iCount) {
409 if (iStartIndex < 0 || iStartIndex > (
int)sStrings.size())
411 if (iCount < 0 || (iStartIndex + iCount) > (
int)sStrings.size())
414 const String* pStrs = sStrings.begin();
417 for (
int i = iStartIndex; i < iStartIndex + iCount; i++) {
418 sJoinStr.
Append(pStrs[i]);
419 if (i != iStartIndex + iCount - 1) {
420 sJoinStr.
Append(chSeparator);
427 std::initializer_list<String> sStrings) {
428 return Join(sSeparator, sStrings, 0, (
int)sStrings.size());
432 std::initializer_list<String> sStrings,
int iStartIndex,
434 if (iStartIndex < 0 || iStartIndex > (
int)sStrings.size())
436 if (iCount < 0 || (iStartIndex + iCount) > (
int)sStrings.size())
439 const String* pStrs = sStrings.begin();
442 for (
int i = iStartIndex; i < iStartIndex + iCount; i++) {
443 sJoinStr.
Append(pStrs[i]);
444 if (i != iStartIndex + iCount - 1) {
445 sJoinStr.
Append(sSeparator);
455 auto pos = m_pImpl->s.rfind(sStr.m_pImpl->s);
456 return (pos == std::string::npos) ? -1 : (int)pos;
459 auto it = std::find_end(
460 m_pImpl->s.begin(), m_pImpl->s.end(),
461 sStr.m_pImpl->s.begin(), sStr.m_pImpl->s.end(),
462 [](
char c1,
char c2) { return std::tolower(static_cast<unsigned char>(c1)) == std::tolower(static_cast<unsigned char>(c2)); }
465 return (it == m_pImpl->s.end()) ? -1 : (int)std::distance(m_pImpl->s.begin(), it);
468 static int FindLastCharIndex(
const std::string& s,
int iStartIndex,
char ch,
bool bIgnoreCase) {
469 for (
int i =
static_cast<int>(s.length()) - 1; i >= iStartIndex; --i) {
470 bool match = bIgnoreCase
471 ? std::tolower(
static_cast<unsigned char>(s[i])) == std::tolower(
static_cast<unsigned char>(ch))
482 for (
auto ch : chChars) {
484 if (idx != -1)
return idx;
489 return PadLeft(iTotalWidth, (
char)
' ');
493 if (iTotalWidth < 0) {
497 if (iTotalWidth <= iLen) {
500 std::string sPadding(iTotalWidth - iLen, ch);
501 m_pImpl->s.insert(0, sPadding);
505 return PadRight(iTotalWidth, (
char)
' ');
509 if (iTotalWidth < 0) {
513 if (iTotalWidth <= iLen) {
516 m_pImpl->s.append(iTotalWidth - iLen, ch);
525 if (iStartIndex < 0 || iStartIndex > iLen || iCount < 0 || (iStartIndex + iCount) > iLen) {
529 std::string sRet = m_pImpl->s;
530 sRet.erase(iStartIndex, iCount);
531 return String(sRet.c_str());
534 char chReplaceChar)
const {
535 std::string sRet = m_pImpl->s;
536 std::replace(sRet.begin(), sRet.end(), chOriginalChar, chReplaceChar);
537 return String(sRet.c_str());
540 const String& sOriginalStr,
541 const String& sReplaceStr)
const {
542 if (sOriginalStr.
IsEmpty())
return *
this;
544 std::string sRet = m_pImpl->s;
546 while ((pos = sRet.find(sOriginalStr.m_pImpl->s, pos)) != std::string::npos) {
547 sRet.replace(pos, sOriginalStr.
GetLength(), sReplaceStr.m_pImpl->s);
550 return String(sRet.c_str());
553 std::vector<String> vTempResult;
554 std::stringstream ss(m_pImpl->s);
556 while (std::getline(ss, sToken, chSeparator)) {
557 vTempResult.push_back(
String(sToken.c_str()));
561 for (
int i = 0; i < (int)vTempResult.size(); i++) result [i] = vTempResult [i];
566 String s(sCurrent.c_str());
569 vResult.push_back(s);
574 std::set<char> charSet;
575 if (!pSeparator || iCount <= 0)
return charSet;
576 for (
int i = 0; i < iCount; ++i) {
578 if (raw) {
while (*raw) charSet.insert(*raw++); }
584 std::vector<String> vResult;
585 std::string sCurrent;
586 for (
char c : sText) {
587 if (charSet.find(c) == charSet.end()) {
604 std::vector<String> vTempResult =
SplitByCharSet(m_pImpl->s, charSet, eOptions);
606 for (
int i = 0; i < result.
GetLength(); i++) result[i] = vTempResult[i];
618 return m_pImpl->s.compare(0, sPrefix.
GetLength(), sPrefix.m_pImpl->s) == 0;
629 return ToStringHelper(iValue);
634 return ToStringHelper(llValue);
638 std::ostringstream ss;
640 return String(ss.str().c_str());
644 return ToStringHelper(iValue);
648 return ToStringHelper(llValue);
652 return ToStringHelper(dValue);
656 return ToStringHelper(bValue);
665 if (iStartIndex < 0 || iStartIndex > iLen || iLength < 0 || (iStartIndex + iLength) > iLen) {
668 return String(m_pImpl->s.substr(iStartIndex, iLength).c_str());
673 std::string sRet = m_pImpl->s;
674 std::transform(sRet.begin(), sRet.end(), sRet.begin(), [](
char ch) ->
char {
675 return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
677 return String(sRet.c_str());
681 std::string sRet = m_pImpl->s;
682 std::transform(sRet.begin(), sRet.end(), sRet.begin(), [](
char ch) ->
char {
683 return static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
685 return String(sRet.c_str());
693 auto it = std::find_if(m_pImpl->s.begin(), m_pImpl->s.end(), [](
char ch) {
694 return !std::isspace(static_cast<unsigned char>(ch));
696 if (it == m_pImpl->s.end())
return "";
697 return String(m_pImpl->s.substr(std::distance(m_pImpl->s.begin(), it)).c_str());
701 auto it = std::find_if(m_pImpl->s.rbegin(), m_pImpl->s.rend(), [](
char ch) {
702 return !std::isspace(static_cast<unsigned char>(ch));
704 if (it == m_pImpl->s.rend())
return "";
705 return String(m_pImpl->s.substr(0, m_pImpl->s.length() - std::distance(m_pImpl->s.rbegin(), it)).c_str());
Defines the exception thrown when an invalid argument is provided to a method.
Defines the exception thrown when an argument value is outside the acceptable range of values.
Provides methods for creating, manipulating, searching, and sorting arrays.
Defines the exception thrown when a requested method or operation is not implemented.
Defines the exception thrown when an arithmetic, casting, or conversion operation results in an overf...
High-performance UTF-8 / UTF-16 string manipulation class mirroring .NET System.String.
Utility routines for high-performance UTF-8, UTF-16, and wide-character string conversions.
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
ArgumentOutOfRangeException(const String &sMessage)
Initializes a new instance of the ArgumentOutOfRangeException class with a specified error message.
Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the ba...
int GetLength() const
Gets the total number of elements in all dimensions of the Array.
T * GetData()
Gets a pointer to the contiguous internal element buffer.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
static String ValueOf(int iValue)
static String Join(char chSeparator, std::initializer_list< String > sStrings)
String & operator+=(const String &sStr)
String & operator=(const String &sStr)
Copy assignment operator.
String Concat(const std::initializer_list< String > sStrs) const
String Substring(int iStartIndex) const
const char * GetChars() const
String operator+(const String &sStr) const
int CompareTo(const String &sStr) const
bool StartsWith(const String &sPrefix) const
String PadLeft(int iTotalWidth)
static int Compare(const String &sStr1, int iIndex1, const String &sStr2, int iIndex2, int iLength, bool bIgnoreCase)
String Replace(char chOriginalChar, char chReplaceChar) const
String & Insert(int iIndex, const String &sStr)
void CopyTo(int iSourceIndex, char *pDestination, int iDestinationIndex, int iDestArraySize, int iCount) const
static String FromInt(int iValue)
String Remove(int iStartIndex) const
bool EndsWith(char ch, bool bIgnoreCase) const
int LastIndexOfAny(int iStartIndex, std::initializer_list< char > chChars, bool bIgnoreCase)
static String ToString(int iValue)
String()
Initializes a new instance of the String class to an empty string.
String & Append(const char ch)
int IndexOfAny(int iStartIndex, std::initializer_list< char > chChars)
static bool Equals(const String &sStr1, const String &sStr2)
int IndexOf(const String &sSubstring) const
static String InternalFormat(const char *pFormat, const String *pArgs, int iArgCount)
const char * GetRawString() const
int LastIndexOf(const String &sStr, bool bIgnoreCase)
bool Contains(char ch) const
char operator[](int iIndex) const
Array< String > Split(char chSeparator) const
String PadRight(int iTotalWidth)
static std::string WCharToUtf8(const wchar_t *pWStr)
Converts a null-terminated UTF-16 wchar_t string into a UTF-8 std::string.
StringSplitOptions
Specifies whether applicable Overload:Split methods include or omit empty substrings.
@ TrimEntries
Trim white-space characters from each element in the array.
@ RemoveEmptyEntries
Omit substrings that contain an empty string from the array.
static void AppendExplicitArg(const std::string &sFmt, size_t i, size_t end, const String *pArgs, int iArgCount, std::string &sRes)
static std::string ToLowerString(const std::string &s)
static void AddSplitToken(const std::string &sCurrent, StringSplitOptions eOptions, std::vector< String > &vResult)
static std::vector< String > SplitByCharSet(const std::string &sText, const std::set< char > &charSet, StringSplitOptions eOptions)
static std::set< char > PopulateSplitCharSet(const String *pSeparator, int iCount)
void ThrowFormatException(const char *msg)
void ThrowArgumentException(const char *msg)
static int FindLastCharIndex(const std::string &s, int iStartIndex, char ch, bool bIgnoreCase)
static void FormatOpenBrace(const std::string &sFmt, size_t &i, int &iAutoIndex, const String *pArgs, int iArgCount, std::string &sRes)
static void FormatClosingBrace(const std::string &sFmt, size_t &i, std::string &sRes)