1 #ifndef CONVERSION_UTILITIES_STRINGCONVERSION_H
2 #define CONVERSION_UTILITIES_STRINGCONVERSION_H
7 #include "../misc/traits.h"
11 #include <initializer_list>
17 #include <string_view>
18 #include <system_error>
21 #if __cplusplus >= 201709
37 std::free(stringData);
44 using StringData = std::pair<std::unique_ptr<char[], StringDataDeleter>, std::size_t>;
48 const char *fromCharset,
const char *toCharset,
const char *inputBuffer, std::size_t inputBufferSize,
float outputBufferSizeFactor = 1.0f);
56 #ifdef PLATFORM_WINDOWS
57 using WideStringData = std::pair<std::unique_ptr<wchar_t[]>,
int>;
58 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec,
const char *inputBuffer,
int inputBufferSize = -1);
59 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec,
const std::string &inputBuffer);
60 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(
const char *inputBuffer,
int inputBufferSize = -1);
68 #if __cplusplus >= 201709
69 template <
class Container>
70 using ContainerValueType =
typename std::conditional_t<std::ranges::range<Container>,
71 std::iterator_traits<std::remove_cvref_t<std::ranges::iterator_t<Container>>>, Container>::value_type;
73 template <
class Container>
using ContainerValueType =
typename Container::value_type;
75 template <
class Container>
using DefaultReturnTypeForContainer = ContainerValueType<Container>;
76 template <
class Container>
using StringParamForContainer = std::basic_string_view<typename ContainerValueType<Container>::value_type>;
94 template <
class Container = std::initializer_list<std::
string>,
class ReturnType = Detail::DefaultReturnTypeForContainer<Container>>
95 ReturnType
joinStrings(
const Container &strings, Detail::StringParamForContainer<Container> delimiter = Detail::StringParamForContainer<Container>(),
96 bool omitEmpty =
false, Detail::StringParamForContainer<Container> leftClosure = Detail::StringParamForContainer<Container>(),
97 Detail::StringParamForContainer<Container> rightClosure = Detail::StringParamForContainer<Container>())
100 if (!strings.size()) {
103 std::size_t entries = 0, size = 0;
104 for (
const auto &str : strings) {
105 if (omitEmpty && str.empty()) {
114 size += (entries * leftClosure.size()) + (entries * rightClosure.size()) + ((entries - 1) * delimiter.size());
116 for (
const auto &str : strings) {
117 if (omitEmpty && str.empty()) {
121 res.append(delimiter);
123 res.append(leftClosure);
125 res.append(rightClosure);
133 template <
class Container = std::initializer_list<std::
string>>
inline auto toMultiline(
const Container &arrayOfLines)
156 template <
class Container = std::list<std::
string>>
157 Container
splitString(Detail::StringParamForContainer<Container>
string, Detail::StringParamForContainer<Container> delimiter,
162 typename Container::value_type *last =
nullptr;
164 typename Container::value_type::size_type
i = 0, end =
string.size();
165 for (
typename Container::value_type::size_type delimPos;
i < end;
i = delimPos + delimiter.size()) {
166 delimPos =
string.find(delimiter,
i);
167 if (!merge && maxParts >= 0 && res.size() ==
static_cast<typename Container::value_type::size_type
>(maxParts)) {
174 delimPos = Container::value_type::npos;
176 if (delimPos == Container::value_type::npos) {
177 delimPos =
string.size();
181 last->append(delimiter);
182 last->append(
string,
i, delimPos -
i);
185 last = &res.emplace_back(
string,
i, delimPos -
i);
208 template <
class Container = std::list<std::
string>>
210 Detail::StringParamForContainer<Container>
string, Detail::StringParamForContainer<Container> delimiter,
int maxParts = -1)
214 typename Container::value_type::size_type
i = 0, end =
string.size();
215 for (
typename Container::value_type::size_type delimPos;
i < end;
i = delimPos + delimiter.size()) {
216 delimPos =
string.find(delimiter,
i);
217 if (maxParts >= 0 && res.size() ==
static_cast<typename Container::value_type::size_type
>(maxParts)) {
218 delimPos = Container::value_type::npos;
220 if (delimPos == Container::value_type::npos) {
221 delimPos =
string.size();
223 #if __cplusplus >= 201709
224 if constexpr (requires { res.emplace_back(
string); }) {
226 res.emplace_back(
string.data() +
i, delimPos -
i);
227 #if __cplusplus >= 201709
229 res.emplace(
string.data() +
i, delimPos -
i);
234 #if __cplusplus >= 201709
235 if constexpr (requires { res.emplace_back(); }) {
238 #if __cplusplus >= 201709
250 template <
class Container = std::vector<std::
string>>
inline auto toArrayOfLines(
const std::string &multilineString)
258 template <
typename StringType>
bool startsWith(
const StringType &str,
const StringType &phrase)
260 if (str.size() < phrase.size()) {
263 for (
auto stri = str.cbegin(), strend = str.cend(), phrasei = phrase.cbegin(), phraseend = phrase.cend();; ++stri, ++phrasei) {
264 if (phrasei == phraseend) {
266 }
else if (stri == strend) {
268 }
else if (*stri != *phrasei) {
278 template <
typename StringType>
bool startsWith(
const StringType &str,
const typename StringType::value_type *phrase)
280 for (
auto stri = str.cbegin(), strend = str.cend();; ++stri, ++phrase) {
283 }
else if (stri == strend) {
285 }
else if (*stri != *phrase) {
295 template <
typename StringType>
bool endsWith(
const StringType &str,
const StringType &phrase)
297 if (str.size() < phrase.size()) {
300 for (
auto stri = str.cend() -
static_cast<typename StringType::difference_type
>(phrase.size()), strend = str.cend(), phrasei = phrase.cbegin();
301 stri != strend; ++stri, ++phrasei) {
302 if (*stri != *phrasei) {
312 template <
typename StringType>
bool endsWith(
const StringType &str,
const typename StringType::value_type *phrase)
314 const auto phraseSize = std::strlen(phrase);
315 if (str.size() < phraseSize) {
318 for (
auto stri = str.cend() -
static_cast<typename StringType::difference_type
>(phraseSize), strend = str.cend(); stri != strend;
320 if (*stri != *phrase) {
331 template <
typename StringType>
bool containsSubstrings(
const StringType &str, std::initializer_list<StringType> substrings)
333 typename StringType::size_type currentPos = 0;
334 for (
const auto &substr : substrings) {
335 if ((currentPos = str.find(substr, currentPos)) == StringType::npos) {
338 currentPos += substr.size();
347 template <
typename StringType>
348 bool containsSubstrings(
const StringType &str, std::initializer_list<const typename StringType::value_type *> substrings)
350 typename StringType::size_type currentPos = 0;
351 for (
const auto *substr : substrings) {
352 if ((currentPos = str.find(substr, currentPos)) == StringType::npos) {
355 currentPos += std::strlen(substr);
363 template <
typename StringType1,
typename StringType2,
typename StringType3>
364 void findAndReplace(StringType1 &str,
const StringType2 &find,
const StringType3 &replace)
366 for (
typename StringType1::size_type
i = 0; (
i = str.find(find,
i)) != StringType1::npos;
i += replace.size()) {
367 str.replace(
i, find.size(), replace);
374 template <
typename StringType>
375 inline void findAndReplace(StringType &str,
const typename StringType::value_type *find,
const typename StringType::value_type *replace)
378 str, std::basic_string_view<typename StringType::value_type>(find), std::basic_string_view<typename StringType::value_type>(replace));
384 template <
typename StringType1,
typename StringType2>
385 inline void findAndReplace(StringType1 &str,
const StringType2 &find,
const typename StringType1::value_type *replace)
387 findAndReplace(str, find, std::basic_string_view<typename StringType1::value_type>(replace));
393 template <
typename StringType1,
typename StringType2>
394 inline void findAndReplace(StringType1 &str,
const typename StringType1::value_type *find,
const StringType2 &replace)
396 findAndReplace(str, std::basic_string_view<typename StringType1::value_type>(find), replace);
405 template <
typename CharType> constexpr CharType
digitToChar(CharType digit)
407 return digit <= 9 ? (digit +
'0') : (digit +
'A' - 10);
416 template <
typename IntegralType,
class StringType = std::string,
typename BaseType = IntegralType,
420 std::size_t resSize = 0;
421 for (
auto n = number; n; n /=
static_cast<IntegralType
>(base), ++resSize)
424 res.reserve(resSize);
426 res.insert(res.begin(), digitToChar<typename StringType::value_type>(
static_cast<typename StringType::value_type
>(number % base)));
427 number /=
static_cast<IntegralType
>(base);
438 template <
typename IntegralType,
class StringType = std::string,
typename BaseType = IntegralType,
439 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * =
nullptr>
440 StringType
numberToString(IntegralType number, BaseType base = 10)
442 const bool negative = number < 0;
445 number = -number, resSize = 1;
449 for (
auto n = number; n; n /=
static_cast<IntegralType
>(base), ++resSize)
452 res.reserve(resSize);
454 res.insert(res.begin(),
455 digitToChar<typename StringType::value_type>(
static_cast<typename StringType::value_type
>(number %
static_cast<IntegralType
>(base))));
456 number /=
static_cast<IntegralType
>(base);
459 res.insert(res.begin(),
'-');
472 template <
typename FloatingType,
class StringType = std::
string, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
475 std::basic_stringstream<typename StringType::value_type> ss;
476 ss << std::setbase(base) << number;
484 template <
typename CharType> CharType
charToDigit(CharType character, CharType base)
487 if (character >=
'0' && character <=
'9') {
488 res = character -
'0';
489 }
else if (character >=
'a' && character <=
'z') {
490 res = character -
'a' + 10;
491 }
else if (character >=
'A' && character <=
'Z') {
492 res = character -
'A' + 10;
497 std::string errorMsg;
498 errorMsg.reserve(36);
499 errorMsg +=
"The character \"";
500 errorMsg += character >=
' ' && character <= '~' ? static_cast<std::string::value_type>(character) :
'?';
501 errorMsg +=
"\" is no valid digit.";
512 template <
typename IntegralType,
class StringType,
typename BaseType = IntegralType,
513 Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>>
517 IntegralType result = 0;
518 for (
const auto &c :
string) {
522 result *=
static_cast<IntegralType
>(base);
523 result +=
static_cast<IntegralType
>(charToDigit<typename StringType::value_type>(c,
static_cast<typename StringType::value_type
>(base)));
535 template <
typename IntegralType,
class StringType,
typename BaseType = IntegralType,
536 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * =
nullptr>
539 auto i =
string.begin();
540 auto end =
string.end();
541 for (;
i != end && *
i ==
' '; ++
i)
546 const bool negative = (*
i ==
'-');
550 IntegralType result = 0;
551 for (;
i != end; ++
i) {
555 result *=
static_cast<IntegralType
>(base);
556 result +=
static_cast<IntegralType
>(charToDigit<typename StringType::value_type>(*
i,
static_cast<typename StringType::value_type
>(base)));
558 return negative ? -result : result;
570 template <
typename FloatingType,
class StringType,
571 Traits::EnableIf<std::is_floating_point<FloatingType>, Traits::Not<std::is_scalar<std::decay_t<StringType>>>> * =
nullptr>
574 std::basic_stringstream<typename StringType::value_type> ss;
575 ss << std::setbase(base) << string;
577 if ((ss >> result) && ss.eof()) {
580 std::string errorMsg;
581 errorMsg.reserve(42 +
string.size());
582 errorMsg +=
"The string \"";
584 errorMsg +=
"\" is no valid floating number.";
595 template <
typename IntegralType,
typename CharType,
typename BaseType = IntegralType,
596 Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * =
nullptr>
599 IntegralType result = 0;
600 for (; *string; ++string) {
601 if (*
string ==
' ') {
604 result *=
static_cast<IntegralType
>(base);
605 result +=
static_cast<IntegralType
>(charToDigit<CharType>(*
string,
static_cast<CharType
>(base)));
619 template <
typename FloatingType,
class CharType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
622 std::basic_stringstream<CharType> ss;
623 ss << std::setbase(base) << string;
625 if ((ss >> result) && ss.eof()) {
628 std::string errorMsg;
629 errorMsg.reserve(42 + std::char_traits<CharType>::length(
string));
630 errorMsg +=
"The string \"";
632 errorMsg +=
"\" is no valid floating number.";
643 template <
typename IntegralType,
class CharType,
typename BaseType = IntegralType,
644 Traits::EnableIf<std::is_integral<IntegralType>, std::is_unsigned<IntegralType>> * =
nullptr>
645 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size, BaseType base = 10)
647 IntegralType result = 0;
648 for (
const CharType *end =
string + size;
string != end; ++string) {
649 if (*
string ==
' ') {
652 result *=
static_cast<IntegralType
>(base);
653 result +=
static_cast<IntegralType
>(charToDigit<CharType>(*
string,
static_cast<CharType
>(base)));
665 template <
typename IntegralType,
typename CharType,
typename BaseType = IntegralType,
666 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * =
nullptr>
672 for (; *
string && *
string ==
' '; ++string)
677 const bool negative = (*
string ==
'-');
681 IntegralType result = 0;
682 for (; *string; ++string) {
683 if (*
string ==
' ') {
686 result *=
static_cast<IntegralType
>(base);
687 result +=
static_cast<IntegralType
>(charToDigit<CharType>(*
string,
static_cast<CharType
>(base)));
689 return negative ? -result : result;
699 template <
typename IntegralType,
typename CharType,
typename BaseType = IntegralType,
700 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * =
nullptr>
701 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size, BaseType base = 10)
706 const CharType *end =
string + size;
707 for (;
string != end && *
string ==
' '; ++string)
712 const bool negative = (*
string ==
'-');
716 IntegralType result = 0;
717 for (;
string != end; ++string) {
718 if (*
string ==
' ') {
721 result *=
static_cast<IntegralType
>(base);
722 result +=
static_cast<IntegralType
>(charToDigit<CharType>(*
string,
static_cast<CharType
>(base)));
724 return negative ? -result : result;
738 char buffer[
sizeof(T)];
739 BE::getBytes(integer, buffer);
740 return std::string(buffer + startOffset,
sizeof(T) -
static_cast<std::size_t
>(startOffset));
The ConversionException class is thrown by the various conversion functions of this library when a co...
#define CPP_UTILITIES_EXPORT
Marks the symbol to be exported by the c++utilities library.
typename std::enable_if< All< Condition... >::value, Detail::Enabler >::type EnableIf
Shortcut for std::enable_if to omit ::value and ::type.
Contains all utilities provides by the c++utilities library.
void findAndReplace(StringType1 &str, const StringType2 &find, const StringType3 &replace)
Replaces all occurences of find with relpace in the specified str.
CPP_UTILITIES_EXPORT StringData convertUtf8ToUtf16BE(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-8 string to UTF-16 (big-endian).
CPP_UTILITIES_EXPORT StringData convertString(const char *fromCharset, const char *toCharset, const char *inputBuffer, std::size_t inputBufferSize, float outputBufferSizeFactor=1.0f)
Converts the specified string from one character set to another.
StringType numberToString(IntegralType number, BaseType base=10)
Converts the given number to its equivalent string representation using the specified base.
CPP_UTILITIES_EXPORT StringData convertLatin1ToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified Latin-1 string to UTF-8.
CPP_UTILITIES_EXPORT StringData convertUtf16LEToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-16 (little-endian) string to UTF-8.
EmptyPartsTreat
Specifies the role of empty parts when splitting strings.
CPP_UTILITIES_EXPORT std::pair< std::unique_ptr< std::uint8_t[]>, std::uint32_t > decodeBase64(const char *encodedStr, const std::uint32_t strSize)
Decodes the specified Base64 encoded string.
ReturnType joinStrings(const Container &strings, Detail::StringParamForContainer< Container > delimiter=Detail::StringParamForContainer< Container >(), bool omitEmpty=false, Detail::StringParamForContainer< Container > leftClosure=Detail::StringParamForContainer< Container >(), Detail::StringParamForContainer< Container > rightClosure=Detail::StringParamForContainer< Container >())
Joins the given strings using the specified delimiter.
bool startsWith(const StringType &str, const StringType &phrase)
Returns whether str starts with phrase.
IntegralType stringToNumber(const StringType &string, BaseType base=10)
Converts the given string to an unsigned number assuming string uses the specified base.
CPP_UTILITIES_EXPORT void truncateString(std::string &str, char terminationChar='\0')
Truncates all characters after the first occurrence of the specified terminationChar and the terminat...
std::pair< std::unique_ptr< char[], StringDataDeleter >, std::size_t > StringData
Type used to return string encoding conversion result.
CPP_UTILITIES_EXPORT StringData convertUtf16BEToUtf8(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-16 (big-endian) string to UTF-8.
constexpr CharType digitToChar(CharType digit)
Returns the character representation of the specified digit.
Container splitStringSimple(Detail::StringParamForContainer< Container > string, Detail::StringParamForContainer< Container > delimiter, int maxParts=-1)
Splits the given string (which might also be a string view) at the specified delimiter.
auto toArrayOfLines(const std::string &multilineString)
Converts the specified multilineString to an array of lines.
bool containsSubstrings(const StringType &str, std::initializer_list< StringType > substrings)
Returns whether str contains the specified substrings.
CPP_UTILITIES_EXPORT StringData convertUtf8ToLatin1(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-8 string to Latin-1.
std::string interpretIntegerAsString(T integer, int startOffset=0)
Interprets the given integer at the specified position as std::string using the specified byte order.
bool endsWith(const StringType &str, const StringType &phrase)
Returns whether str ends with phrase.
auto toMultiline(const Container &arrayOfLines)
Converts the specified arrayOfLines to a multiline string.
CPP_UTILITIES_EXPORT StringData convertUtf8ToUtf16LE(const char *inputBuffer, std::size_t inputBufferSize)
Converts the specified UTF-8 string to UTF-16 (little-endian).
CPP_UTILITIES_EXPORT std::string bitrateToString(double speedInKbitsPerSecond, bool useByteInsteadOfBits=false)
Converts the specified bitrate in kbit/s to its equivalent std::string representation.
CharType charToDigit(CharType character, CharType base)
Returns number/digit of the specified character representation using the specified base.
CPP_UTILITIES_EXPORT std::string encodeBase64(const std::uint8_t *data, std::uint32_t dataSize)
Encodes the specified data to Base64.
CPP_UTILITIES_EXPORT std::string dataSizeToString(std::uint64_t sizeInByte, bool includeByte=false)
Converts the specified data size in byte to its equivalent std::string representation.
IntegralType bufferToNumber(const CharType *string, std::size_t size, BaseType base=10)
Converts the given string of size characters to an unsigned numeric value using the specified base.
Container splitString(Detail::StringParamForContainer< Container > string, Detail::StringParamForContainer< Container > delimiter, EmptyPartsTreat emptyPartsRole=EmptyPartsTreat::Keep, int maxParts=-1)
Splits the given string at the specified delimiter.
The StringDataDeleter struct deletes the data of a StringData instance.
void operator()(char *stringData)
Deletes the specified stringData with std::free(), because the memory has been allocated using std::m...