You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1056 lines
25 KiB
C++
1056 lines
25 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
|
|
#include <platform.h>
|
|
#include "StringHelpers.h"
|
|
#include "Util.h"
|
|
#include "StringUtils.h" // From CryCommon
|
|
#include "UnicodeFunctions.h"
|
|
#include <cctype>
|
|
#include <algorithm>
|
|
|
|
#include <AzCore/PlatformIncl.h> // WideCharToMultibyte(), CP_UTF8, etc.
|
|
#include <AzCore/Casting/numeric_cast.h>
|
|
|
|
static inline char ToLower(const char c)
|
|
{
|
|
return tolower(c);
|
|
}
|
|
|
|
static inline wchar_t ToLower(const wchar_t c)
|
|
{
|
|
return towlower(c);
|
|
}
|
|
|
|
|
|
static inline char ToUpper(const char c)
|
|
{
|
|
return toupper(c);
|
|
}
|
|
|
|
static inline wchar_t ToUpper(const wchar_t c)
|
|
{
|
|
return towupper(c);
|
|
}
|
|
|
|
|
|
static inline int Vscprintf(const char* format, va_list argList)
|
|
{
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
return _vscprintf(format, argList);
|
|
#elif AZ_TRAIT_OS_PLATFORM_APPLE || defined(AZ_PLATFORM_LINUX)
|
|
int retval;
|
|
va_list argcopy;
|
|
va_copy(argcopy, argList);
|
|
retval = azvsnprintf(NULL, 0, format, argcopy);
|
|
va_end(argcopy);
|
|
return retval;
|
|
#else
|
|
#error Not supported!
|
|
#endif
|
|
}
|
|
|
|
static inline int Vscprintf(const wchar_t* format, va_list argList)
|
|
{
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
return _vscwprintf(format, argList);
|
|
#elif AZ_TRAIT_OS_PLATFORM_APPLE || defined(AZ_PLATFORM_LINUX)
|
|
int retval;
|
|
va_list argcopy;
|
|
va_copy(argcopy, argList);
|
|
retval = azvsnwprintf(NULL, 0, format, argcopy);
|
|
va_end(argcopy);
|
|
return retval;
|
|
#else
|
|
#error Not supported!
|
|
#endif
|
|
}
|
|
|
|
|
|
static inline int Vsnprintf_s(char* str, size_t sizeInBytes, [[maybe_unused]] size_t count, const char* format, va_list argList)
|
|
{
|
|
return azvsnprintf(str, sizeInBytes, format, argList);
|
|
}
|
|
|
|
static inline int Vsnprintf_s(wchar_t* str, size_t sizeInBytes, [[maybe_unused]] size_t count, const wchar_t* format, va_list argList)
|
|
{
|
|
return azvsnwprintf(str, sizeInBytes, format, argList);
|
|
}
|
|
|
|
|
|
int StringHelpers::Compare(const string& str0, const string& str1)
|
|
{
|
|
const size_t minLength = Util::getMin(str0.length(), str1.length());
|
|
const int result = std::memcmp(str0.c_str(), str1.c_str(), minLength);
|
|
if (result)
|
|
{
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return (str0.length() == str1.length())
|
|
? 0
|
|
: ((str0.length() < str1.length()) ? -1 : +1);
|
|
}
|
|
}
|
|
|
|
int StringHelpers::Compare(const wstring& str0, const wstring& str1)
|
|
{
|
|
const size_t minLength = Util::getMin(str0.length(), str1.length());
|
|
for (size_t i = 0; i < minLength; ++i)
|
|
{
|
|
const wchar_t c0 = str0[i];
|
|
const wchar_t c1 = str1[i];
|
|
if (c0 != c1)
|
|
{
|
|
return (c0 < c1) ? -1 : 1;
|
|
}
|
|
}
|
|
|
|
return (str0.length() == str1.length())
|
|
? 0
|
|
: ((str0.length() < str1.length()) ? -1 : +1);
|
|
}
|
|
|
|
|
|
int StringHelpers::CompareIgnoreCase(const string& str0, const string& str1)
|
|
{
|
|
const size_t minLength = Util::getMin(str0.length(), str1.length());
|
|
const int result = azmemicmp(str0.c_str(), str1.c_str(), minLength);
|
|
if (result)
|
|
{
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return (str0.length() == str1.length())
|
|
? 0
|
|
: ((str0.length() < str1.length()) ? -1 : +1);
|
|
}
|
|
}
|
|
|
|
int StringHelpers::CompareIgnoreCase(const wstring& str0, const wstring& str1)
|
|
{
|
|
const size_t minLength = Util::getMin(str0.length(), str1.length());
|
|
for (size_t i = 0; i < minLength; ++i)
|
|
{
|
|
const wchar_t c0 = towlower(str0[i]);
|
|
const wchar_t c1 = towlower(str1[i]);
|
|
if (c0 != c1)
|
|
{
|
|
return (c0 < c1) ? -1 : 1;
|
|
}
|
|
}
|
|
|
|
return (str0.length() == str1.length())
|
|
? 0
|
|
: ((str0.length() < str1.length()) ? -1 : +1);
|
|
}
|
|
|
|
|
|
bool StringHelpers::Equals(const string& str0, const string& str1)
|
|
{
|
|
if (str0.length() != str1.length())
|
|
{
|
|
return false;
|
|
}
|
|
return std::memcmp(str0.c_str(), str1.c_str(), str1.length()) == 0;
|
|
}
|
|
|
|
bool StringHelpers::Equals(const wstring& str0, const wstring& str1)
|
|
{
|
|
if (str0.length() != str1.length())
|
|
{
|
|
return false;
|
|
}
|
|
return std::memcmp(str0.c_str(), str1.c_str(), str1.length() * sizeof(wstring::value_type)) == 0;
|
|
}
|
|
|
|
|
|
bool StringHelpers::EqualsIgnoreCase(const string& str0, const string& str1)
|
|
{
|
|
if (str0.length() != str1.length())
|
|
{
|
|
return false;
|
|
}
|
|
return azmemicmp(str0.c_str(), str1.c_str(), str1.length()) == 0;
|
|
}
|
|
|
|
bool StringHelpers::EqualsIgnoreCase(const wstring& str0, const wstring& str1)
|
|
{
|
|
const size_t str1Length = str1.length();
|
|
if (str0.length() != str1Length)
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < str1Length; ++i)
|
|
{
|
|
if (towlower(str0[i]) != towlower(str1[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool StringHelpers::StartsWith(const string& str, const string& pattern)
|
|
{
|
|
if (str.length() < pattern.length())
|
|
{
|
|
return false;
|
|
}
|
|
return std::memcmp(str.c_str(), pattern.c_str(), pattern.length()) == 0;
|
|
}
|
|
|
|
bool StringHelpers::StartsWith(const wstring& str, const wstring& pattern)
|
|
{
|
|
if (str.length() < pattern.length())
|
|
{
|
|
return false;
|
|
}
|
|
return std::memcmp(str.c_str(), pattern.c_str(), pattern.length() * sizeof(wstring::value_type)) == 0;
|
|
}
|
|
|
|
|
|
bool StringHelpers::StartsWithIgnoreCase(const string& str, const string& pattern)
|
|
{
|
|
if (str.length() < pattern.length())
|
|
{
|
|
return false;
|
|
}
|
|
return azmemicmp(str.c_str(), pattern.c_str(), pattern.length()) == 0;
|
|
}
|
|
|
|
bool StringHelpers::StartsWithIgnoreCase(const wstring& str, const wstring& pattern)
|
|
{
|
|
const size_t patternLength = pattern.length();
|
|
if (str.length() < patternLength)
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = 0; i < patternLength; ++i)
|
|
{
|
|
if (towlower(str[i]) != towlower(pattern[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool StringHelpers::EndsWith(const string& str, const string& pattern)
|
|
{
|
|
if (str.length() < pattern.length())
|
|
{
|
|
return false;
|
|
}
|
|
return std::memcmp(str.c_str() + str.length() - pattern.length(), pattern.c_str(), pattern.length()) == 0;
|
|
}
|
|
|
|
bool StringHelpers::EndsWith(const wstring& str, const wstring& pattern)
|
|
{
|
|
if (str.length() < pattern.length())
|
|
{
|
|
return false;
|
|
}
|
|
return std::memcmp(str.c_str() + str.length() - pattern.length(), pattern.c_str(), pattern.length() * sizeof(wstring::value_type)) == 0;
|
|
}
|
|
|
|
|
|
bool StringHelpers::EndsWithIgnoreCase(const string& str, const string& pattern)
|
|
{
|
|
if (str.length() < pattern.length())
|
|
{
|
|
return false;
|
|
}
|
|
return azmemicmp(str.c_str() + str.length() - pattern.length(), pattern.c_str(), pattern.length()) == 0;
|
|
}
|
|
|
|
bool StringHelpers::EndsWithIgnoreCase(const wstring& str, const wstring& pattern)
|
|
{
|
|
const size_t patternLength = pattern.length();
|
|
if (str.length() < patternLength)
|
|
{
|
|
return false;
|
|
}
|
|
for (size_t i = str.length() - patternLength, j = 0; i < patternLength; ++i, ++j)
|
|
{
|
|
if (towlower(str[i]) != towlower(pattern[j]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool StringHelpers::Contains(const string& str, const string& pattern)
|
|
{
|
|
const size_t patternLength = pattern.length();
|
|
if (str.length() < patternLength)
|
|
{
|
|
return false;
|
|
}
|
|
const size_t n = str.length() - patternLength + 1;
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (std::memcmp(str.c_str() + i, pattern.c_str(), patternLength) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StringHelpers::Contains(const wstring& str, const wstring& pattern)
|
|
{
|
|
const size_t patternLength = pattern.length();
|
|
if (str.length() < patternLength)
|
|
{
|
|
return false;
|
|
}
|
|
const size_t n = str.length() - patternLength + 1;
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (std::memcmp(str.c_str() + i, pattern.c_str(), patternLength * sizeof(wstring::value_type)) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool StringHelpers::ContainsIgnoreCase(const string& str, const string& pattern)
|
|
{
|
|
const size_t patternLength = pattern.length();
|
|
if (str.length() < patternLength)
|
|
{
|
|
return false;
|
|
}
|
|
const size_t n = str.length() - patternLength + 1;
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
if (azmemicmp(str.c_str() + i, pattern.c_str(), patternLength) == 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StringHelpers::ContainsIgnoreCase(const wstring& str, const wstring& pattern)
|
|
{
|
|
const size_t patternLength = pattern.length();
|
|
if (patternLength == 0)
|
|
{
|
|
return true;
|
|
}
|
|
if (str.length() < patternLength)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const wstring::value_type firstPatternLetter = towlower(pattern[0]);
|
|
const size_t n = str.length() - patternLength + 1;
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
bool match = true;
|
|
for (size_t j = 0; j < patternLength; ++j)
|
|
{
|
|
if (towlower(str[i + j]) != towlower(pattern[j]))
|
|
{
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
if (match)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool StringHelpers::MatchesWildcards(const string& str, const string& wildcards)
|
|
{
|
|
using namespace CryStringUtils_Internal;
|
|
return MatchesWildcards_Tpl<SCharComparatorCaseSensitive>(str.c_str(), wildcards.c_str());
|
|
}
|
|
|
|
bool StringHelpers::MatchesWildcards(const wstring& str, const wstring& wildcards)
|
|
{
|
|
using namespace CryStringUtils_Internal;
|
|
return MatchesWildcards_Tpl<SCharComparatorCaseSensitive>(str.c_str(), wildcards.c_str());
|
|
}
|
|
|
|
bool StringHelpers::MatchesWildcardsIgnoreCase(const string& str, const string& wildcards)
|
|
{
|
|
using namespace CryStringUtils_Internal;
|
|
return MatchesWildcards_Tpl<SCharComparatorCaseInsensitive>(str.c_str(), wildcards.c_str());
|
|
}
|
|
|
|
bool StringHelpers::MatchesWildcardsIgnoreCase(const wstring& str, const wstring& wildcards)
|
|
{
|
|
using namespace CryStringUtils_Internal;
|
|
return MatchesWildcards_Tpl<SCharComparatorCaseInsensitive>(str.c_str(), wildcards.c_str());
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline bool MatchesWildcardsIgnoreCaseExt_Tpl(const TS& str, const TS& wildcards, std::vector<TS>& wildcardMatches)
|
|
{
|
|
const typename TS::value_type* savedStrBegin = 0;
|
|
const typename TS::value_type* savedStrEnd = 0;
|
|
const typename TS::value_type* savedWild = 0;
|
|
size_t savedWildCount = 0;
|
|
|
|
const typename TS::value_type* pStr = str.c_str();
|
|
const typename TS::value_type* pWild = wildcards.c_str();
|
|
size_t wildCount = 0;
|
|
|
|
while ((*pStr) && (*pWild != '*'))
|
|
{
|
|
if (*pWild == '?')
|
|
{
|
|
wildcardMatches.push_back(TS(pStr, pStr + 1));
|
|
++wildCount;
|
|
}
|
|
else if (ToLower(*pWild) != ToLower(*pStr))
|
|
{
|
|
wildcardMatches.resize(wildcardMatches.size() - wildCount);
|
|
return false;
|
|
}
|
|
++pWild;
|
|
++pStr;
|
|
}
|
|
|
|
while (*pStr)
|
|
{
|
|
if (*pWild == '*')
|
|
{
|
|
if (!pWild[1])
|
|
{
|
|
wildcardMatches.push_back(TS(pStr));
|
|
return true;
|
|
}
|
|
savedWild = pWild++;
|
|
savedStrBegin = pStr;
|
|
savedStrEnd = pStr;
|
|
savedWildCount = wildCount;
|
|
wildcardMatches.push_back(TS(savedStrBegin, savedStrEnd));
|
|
++wildCount;
|
|
}
|
|
else if (*pWild == '?')
|
|
{
|
|
wildcardMatches.push_back(TS(pStr, pStr + 1));
|
|
++wildCount;
|
|
++pWild;
|
|
++pStr;
|
|
}
|
|
else if (ToLower(*pWild) == ToLower(*pStr))
|
|
{
|
|
++pWild;
|
|
++pStr;
|
|
}
|
|
else
|
|
{
|
|
wildcardMatches.resize(wildcardMatches.size() - (wildCount - savedWildCount));
|
|
wildCount = savedWildCount;
|
|
++savedStrEnd;
|
|
pWild = savedWild + 1;
|
|
pStr = savedStrEnd;
|
|
wildcardMatches.push_back(TS(savedStrBegin, savedStrEnd));
|
|
++wildCount;
|
|
}
|
|
}
|
|
|
|
while (*pWild == '*')
|
|
{
|
|
++pWild;
|
|
wildcardMatches.push_back(TS());
|
|
++wildCount;
|
|
}
|
|
|
|
if (*pWild)
|
|
{
|
|
wildcardMatches.resize(wildcardMatches.size() - wildCount);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StringHelpers::MatchesWildcardsIgnoreCaseExt(const string& str, const string& wildcards, std::vector<string>& wildcardMatches)
|
|
{
|
|
return MatchesWildcardsIgnoreCaseExt_Tpl(str, wildcards, wildcardMatches);
|
|
}
|
|
|
|
bool StringHelpers::MatchesWildcardsIgnoreCaseExt(const wstring& str, const wstring& wildcards, std::vector<wstring>& wildcardMatches)
|
|
{
|
|
return MatchesWildcardsIgnoreCaseExt_Tpl(str, wildcards, wildcardMatches);
|
|
}
|
|
|
|
|
|
string StringHelpers::TrimLeft(const string& s)
|
|
{
|
|
const size_t first = s.find_first_not_of(" \r\t");
|
|
return (first == s.npos) ? string() : s.substr(first);
|
|
}
|
|
|
|
wstring StringHelpers::TrimLeft(const wstring& s)
|
|
{
|
|
const size_t first = s.find_first_not_of(L" \r\t");
|
|
return (first == s.npos) ? wstring() : s.substr(first);
|
|
}
|
|
|
|
|
|
string StringHelpers::TrimRight(const string& s)
|
|
{
|
|
const size_t last = s.find_last_not_of(" \r\t");
|
|
return (last == s.npos) ? s : s.substr(0, last + 1);
|
|
}
|
|
|
|
wstring StringHelpers::TrimRight(const wstring& s)
|
|
{
|
|
const size_t last = s.find_last_not_of(L" \r\t");
|
|
return (last == s.npos) ? s : s.substr(0, last + 1);
|
|
}
|
|
|
|
|
|
string StringHelpers::Trim(const string& s)
|
|
{
|
|
return TrimLeft(TrimRight(s));
|
|
}
|
|
|
|
wstring StringHelpers::Trim(const wstring& s)
|
|
{
|
|
return TrimLeft(TrimRight(s));
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline TS RemoveDuplicateSpaces_Tpl(const TS& s)
|
|
{
|
|
TS res;
|
|
bool spaceFound = false;
|
|
|
|
for (size_t i = 0, n = s.length(); i < n; ++i)
|
|
{
|
|
if ((s[i] == ' ') || (s[i] == '\r') || (s[i] == '\t'))
|
|
{
|
|
spaceFound = true;
|
|
}
|
|
else
|
|
{
|
|
if (spaceFound)
|
|
{
|
|
res += ' ';
|
|
spaceFound = false;
|
|
}
|
|
res += s[i];
|
|
}
|
|
}
|
|
|
|
if (spaceFound)
|
|
{
|
|
res += ' ';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
string StringHelpers::RemoveDuplicateSpaces(const string& s)
|
|
{
|
|
return RemoveDuplicateSpaces_Tpl(s);
|
|
}
|
|
|
|
wstring StringHelpers::RemoveDuplicateSpaces(const wstring& s)
|
|
{
|
|
return RemoveDuplicateSpaces_Tpl(s);
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline TS MakeLowerCase_Tpl(const TS& s)
|
|
{
|
|
TS copy;
|
|
copy.reserve(s.length());
|
|
for (typename TS::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
|
|
{
|
|
copy.append(1, ToLower(*it));
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
string StringHelpers::MakeLowerCase(const string& s)
|
|
{
|
|
return MakeLowerCase_Tpl(s);
|
|
}
|
|
|
|
wstring StringHelpers::MakeLowerCase(const wstring& s)
|
|
{
|
|
return MakeLowerCase_Tpl(s);
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline TS MakeUpperCase_Tpl(const TS& s)
|
|
{
|
|
TS copy;
|
|
copy.reserve(s.length());
|
|
for (typename TS::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
|
|
{
|
|
copy.append(1, ToUpper(*it));
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
string StringHelpers::MakeUpperCase(const string& s)
|
|
{
|
|
return MakeUpperCase_Tpl(s);
|
|
}
|
|
|
|
wstring StringHelpers::MakeUpperCase(const wstring& s)
|
|
{
|
|
return MakeUpperCase_Tpl(s);
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline TS Replace_Tpl(const TS& s, const typename TS::value_type oldChar, const typename TS::value_type newChar)
|
|
{
|
|
TS copy;
|
|
copy.reserve(s.length());
|
|
for (typename TS::const_iterator it = s.begin(), end = s.end(); it != end; ++it)
|
|
{
|
|
const typename TS::value_type c = (*it);
|
|
copy.append(1, ((c == oldChar) ? newChar : c));
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
string StringHelpers::Replace(const string& s, char oldChar, char newChar)
|
|
{
|
|
return Replace_Tpl(s, oldChar, newChar);
|
|
}
|
|
|
|
wstring StringHelpers::Replace(const wstring& s, wchar_t oldChar, wchar_t newChar)
|
|
{
|
|
return Replace_Tpl(s, oldChar, newChar);
|
|
}
|
|
|
|
|
|
void StringHelpers::ConvertStringByRef(string& out, const string& in)
|
|
{
|
|
out = in;
|
|
}
|
|
|
|
void StringHelpers::ConvertStringByRef(wstring& out, const string& in)
|
|
{
|
|
Unicode::Convert(out, in);
|
|
}
|
|
|
|
void StringHelpers::ConvertStringByRef(string& out, const wstring& in)
|
|
{
|
|
Unicode::Convert(out, in);
|
|
}
|
|
|
|
void StringHelpers::ConvertStringByRef(wstring& out, const wstring& in)
|
|
{
|
|
out = in;
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline void Split_Tpl(const TS& str, const TS& separator, bool bReturnEmptyPartsToo, std::vector<TS>& outParts)
|
|
{
|
|
if (str.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (separator.empty())
|
|
{
|
|
for (size_t i = 0; i < str.length(); ++i)
|
|
{
|
|
outParts.push_back(str.substr(i, 1));
|
|
}
|
|
return;
|
|
}
|
|
|
|
size_t partStart = 0;
|
|
|
|
for (;; )
|
|
{
|
|
const size_t pos = str.find(separator, partStart);
|
|
if (pos == TS::npos)
|
|
{
|
|
break;
|
|
}
|
|
if (bReturnEmptyPartsToo || (pos > partStart))
|
|
{
|
|
outParts.push_back(str.substr(partStart, pos - partStart));
|
|
}
|
|
partStart = pos + separator.length();
|
|
}
|
|
|
|
if (bReturnEmptyPartsToo || (partStart < str.length()))
|
|
{
|
|
outParts.push_back(str.substr(partStart, str.length() - partStart));
|
|
}
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline void SplitByAnyOf_Tpl(const TS& str, const TS& separators, bool bReturnEmptyPartsToo, std::vector<TS>& outParts)
|
|
{
|
|
if (str.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (separators.empty())
|
|
{
|
|
for (size_t i = 0; i < str.length(); ++i)
|
|
{
|
|
outParts.push_back(str.substr(i, 1));
|
|
}
|
|
return;
|
|
}
|
|
|
|
size_t partStart = 0;
|
|
|
|
for (;; )
|
|
{
|
|
const size_t pos = str.find_first_of(separators, partStart);
|
|
if (pos == TS::npos)
|
|
{
|
|
break;
|
|
}
|
|
if (bReturnEmptyPartsToo || (pos > partStart))
|
|
{
|
|
outParts.push_back(str.substr(partStart, pos - partStart));
|
|
}
|
|
partStart = pos + 1;
|
|
}
|
|
|
|
if (bReturnEmptyPartsToo || (partStart < str.length()))
|
|
{
|
|
outParts.push_back(str.substr(partStart, str.length() - partStart));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void StringHelpers::Split(const string& str, const string& separator, bool bReturnEmptyPartsToo, std::vector<string>& outParts)
|
|
{
|
|
Split_Tpl(str, separator, bReturnEmptyPartsToo, outParts);
|
|
}
|
|
|
|
void StringHelpers::Split(const wstring& str, const wstring& separator, bool bReturnEmptyPartsToo, std::vector<wstring>& outParts)
|
|
{
|
|
Split_Tpl(str, separator, bReturnEmptyPartsToo, outParts);
|
|
}
|
|
|
|
|
|
void StringHelpers::SplitByAnyOf(const string& str, const string& separators, bool bReturnEmptyPartsToo, std::vector<string>& outParts)
|
|
{
|
|
SplitByAnyOf_Tpl(str, separators, bReturnEmptyPartsToo, outParts);
|
|
}
|
|
|
|
void StringHelpers::SplitByAnyOf(const wstring& str, const wstring& separators, bool bReturnEmptyPartsToo, std::vector<wstring>& outParts)
|
|
{
|
|
SplitByAnyOf_Tpl(str, separators, bReturnEmptyPartsToo, outParts);
|
|
}
|
|
|
|
|
|
template <class TS>
|
|
static inline TS FormatVA_Tpl(const typename TS::value_type* const format, va_list parg)
|
|
{
|
|
if ((format == 0) || (format[0] == 0))
|
|
{
|
|
return TS();
|
|
}
|
|
|
|
std::vector<typename TS::value_type> bf;
|
|
size_t capacity = 0;
|
|
|
|
size_t wantedCapacity = Vscprintf(format, parg);
|
|
wantedCapacity += 2; // '+ 2' to prevent uncertainty when Vsnprintf_s() returns 'size - 1'
|
|
|
|
for (;; )
|
|
{
|
|
if (wantedCapacity > capacity)
|
|
{
|
|
capacity = wantedCapacity;
|
|
bf.resize(capacity + 1);
|
|
}
|
|
|
|
const int countWritten = Vsnprintf_s(&bf[0], capacity + 1, _TRUNCATE, format, parg);
|
|
|
|
if ((countWritten >= 0) && (capacity > (size_t)countWritten + 1))
|
|
{
|
|
bf[countWritten] = 0;
|
|
return TS(&bf[0]);
|
|
}
|
|
|
|
wantedCapacity = capacity * 2;
|
|
}
|
|
}
|
|
|
|
string StringHelpers::FormatVA(const char* const format, va_list parg)
|
|
{
|
|
return FormatVA_Tpl<string>(format, parg);
|
|
}
|
|
|
|
wstring StringHelpers::FormatVA(const wchar_t* const format, va_list parg)
|
|
{
|
|
return FormatVA_Tpl<wstring>(format, parg);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
template <class TC>
|
|
static inline void SafeCopy_Tpl(TC* const pDstBuffer, const size_t dstBufferSizeInBytes, const TC* const pSrc)
|
|
{
|
|
if (dstBufferSizeInBytes >= sizeof(TC))
|
|
{
|
|
const size_t n = dstBufferSizeInBytes / sizeof(TC) - 1;
|
|
size_t i;
|
|
for (i = 0; i < n && pSrc[i]; ++i)
|
|
{
|
|
pDstBuffer[i] = pSrc[i];
|
|
}
|
|
pDstBuffer[i] = 0;
|
|
}
|
|
}
|
|
|
|
template <class TC>
|
|
static inline void SafeCopyPadZeros_Tpl(TC* const pDstBuffer, const size_t dstBufferSizeInBytes, const TC* const pSrc)
|
|
{
|
|
if (dstBufferSizeInBytes > 0)
|
|
{
|
|
const size_t n = (dstBufferSizeInBytes < sizeof(TC)) ? 0 : dstBufferSizeInBytes / sizeof(TC) - 1;
|
|
size_t i;
|
|
for (i = 0; i < n && pSrc[i]; ++i)
|
|
{
|
|
pDstBuffer[i] = pSrc[i];
|
|
}
|
|
memset(&pDstBuffer[i], 0, dstBufferSizeInBytes - i * sizeof(TC));
|
|
}
|
|
}
|
|
|
|
|
|
void StringHelpers::SafeCopy(char* const pDstBuffer, const size_t dstBufferSizeInBytes, const char* const pSrc)
|
|
{
|
|
SafeCopy_Tpl<char>(pDstBuffer, dstBufferSizeInBytes, pSrc);
|
|
}
|
|
|
|
void StringHelpers::SafeCopy(wchar_t* const pDstBuffer, const size_t dstBufferSizeInBytes, const wchar_t* const pSrc)
|
|
{
|
|
SafeCopy_Tpl<wchar_t>(pDstBuffer, dstBufferSizeInBytes, pSrc);
|
|
}
|
|
|
|
|
|
void StringHelpers::SafeCopyPadZeros(char* const pDstBuffer, const size_t dstBufferSizeInBytes, const char* const pSrc)
|
|
{
|
|
SafeCopyPadZeros_Tpl<char>(pDstBuffer, dstBufferSizeInBytes, pSrc);
|
|
}
|
|
|
|
void StringHelpers::SafeCopyPadZeros(wchar_t* const pDstBuffer, const size_t dstBufferSizeInBytes, const wchar_t* const pSrc)
|
|
{
|
|
SafeCopyPadZeros_Tpl<wchar_t>(pDstBuffer, dstBufferSizeInBytes, pSrc);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool StringHelpers::Utf16ContainsAsciiOnly(const wchar_t* wstr)
|
|
{
|
|
while (*wstr)
|
|
{
|
|
if (*wstr > 127 || *wstr < 0)
|
|
{
|
|
return false;
|
|
}
|
|
++wstr;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
string StringHelpers::ConvertAsciiUtf16ToAscii(const wchar_t* wstr)
|
|
{
|
|
const size_t len = wcslen(wstr);
|
|
|
|
string result;
|
|
result.reserve(len);
|
|
|
|
for (size_t i = 0; i < len; ++i)
|
|
{
|
|
result.push_back(wstr[i] & 0x7F);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
wstring StringHelpers::ConvertAsciiToUtf16(const char* str)
|
|
{
|
|
const size_t len = strlen(str);
|
|
|
|
wstring result;
|
|
result.reserve(len);
|
|
|
|
for (size_t i = 0; i < len; ++i)
|
|
{
|
|
result.push_back(str[i] & 0x7F);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#if defined(AZ_PLATFORM_WINDOWS)
|
|
static string ConvertUtf16ToMultibyte(const wchar_t* wstr, uint codePage, char badChar)
|
|
{
|
|
if (wstr[0] == 0)
|
|
{
|
|
return string();
|
|
}
|
|
|
|
const int len = aznumeric_caster(wcslen(wstr));
|
|
|
|
// Request needed buffer size, in bytes
|
|
int neededByteCount = WideCharToMultiByte(
|
|
codePage,
|
|
0,
|
|
wstr,
|
|
len,
|
|
0,
|
|
0,
|
|
((badChar && codePage != CP_UTF8) ? &badChar : NULL),
|
|
NULL);
|
|
if (neededByteCount <= 0)
|
|
{
|
|
return string();
|
|
}
|
|
++neededByteCount; // extra space for terminating zero
|
|
|
|
std::vector<char> buffer(neededByteCount);
|
|
|
|
const int byteCount = WideCharToMultiByte(
|
|
codePage,
|
|
0,
|
|
wstr,
|
|
len,
|
|
&buffer[0], // output buffer
|
|
neededByteCount - 1, // size of the output buffer in bytes
|
|
((badChar && codePage != CP_UTF8) ? &badChar : NULL),
|
|
NULL);
|
|
if (byteCount != neededByteCount - 1)
|
|
{
|
|
return string();
|
|
}
|
|
|
|
buffer[byteCount] = 0;
|
|
|
|
return string(&buffer[0]);
|
|
}
|
|
|
|
|
|
static wstring ConvertMultibyteToUtf16(const char* str, uint codePage)
|
|
{
|
|
if (str[0] == 0)
|
|
{
|
|
return wstring();
|
|
}
|
|
|
|
const int len = aznumeric_caster(strlen(str));
|
|
|
|
// Request needed buffer size, in characters
|
|
int neededCharCount = MultiByteToWideChar(
|
|
codePage,
|
|
0,
|
|
str,
|
|
len,
|
|
0,
|
|
0);
|
|
if (neededCharCount <= 0)
|
|
{
|
|
return wstring();
|
|
}
|
|
++neededCharCount; // extra space for terminating zero
|
|
|
|
std::vector<wchar_t> wbuffer(neededCharCount);
|
|
|
|
const int charCount = MultiByteToWideChar(
|
|
codePage,
|
|
0,
|
|
str,
|
|
len,
|
|
&wbuffer[0], // output buffer
|
|
neededCharCount - 1); // size of the output buffer in characters
|
|
if (charCount != neededCharCount - 1)
|
|
{
|
|
return wstring();
|
|
}
|
|
|
|
wbuffer[charCount] = 0;
|
|
|
|
return wstring(&wbuffer[0]);
|
|
}
|
|
|
|
|
|
wstring StringHelpers::ConvertUtf8ToUtf16(const char* str)
|
|
{
|
|
return ConvertMultibyteToUtf16(str, CP_UTF8);
|
|
}
|
|
|
|
|
|
string StringHelpers::ConvertUtf16ToUtf8(const wchar_t* wstr)
|
|
{
|
|
return ConvertUtf16ToMultibyte(wstr, CP_UTF8, 0);
|
|
}
|
|
|
|
|
|
wstring StringHelpers::ConvertAnsiToUtf16(const char* str)
|
|
{
|
|
return ConvertMultibyteToUtf16(str, CP_ACP);
|
|
}
|
|
|
|
|
|
string StringHelpers::ConvertUtf16ToAnsi(const wchar_t* wstr, char badChar)
|
|
{
|
|
return ConvertUtf16ToMultibyte(wstr, CP_ACP, badChar);
|
|
}
|
|
#endif //AZ_PLATFORM_WINDOWS
|
|
|
|
string StringHelpers::ConvertAnsiToAscii(const char* str, char badChar)
|
|
{
|
|
const size_t len = strlen(str);
|
|
|
|
string result;
|
|
result.reserve(len);
|
|
|
|
for (size_t i = 0; i < len; ++i)
|
|
{
|
|
char c = str[i];
|
|
if (c < 0 || c > 127)
|
|
{
|
|
c = badChar;
|
|
}
|
|
result.push_back(c);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// eof
|