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>
33 std::free(stringData);
40 using StringData = std::pair<std::unique_ptr<char[], StringDataDeleter>, std::size_t>;
44 const char *fromCharset,
const char *toCharset,
const char *inputBuffer, std::size_t inputBufferSize,
float outputBufferSizeFactor = 1.0f);
52 #ifdef PLATFORM_WINDOWS
53 using WideStringData = std::pair<std::unique_ptr<wchar_t[]>,
int>;
54 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec,
const char *inputBuffer,
int inputBufferSize = -1);
55 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec,
const std::string &inputBuffer);
56 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(
const char *inputBuffer,
int inputBufferSize = -1);
75 template <
class Container = std::initializer_list<std::
string>,
class ReturnType =
typename Container::value_type>
76 ReturnType
joinStrings(
const Container &strings,
const typename Container::value_type &delimiter =
typename Container::value_type(),
77 bool omitEmpty =
false,
const typename Container::value_type &leftClosure =
typename Container::value_type(),
78 const typename Container::value_type &rightClosure =
typename Container::value_type())
81 if (!strings.size()) {
84 std::size_t entries = 0, size = 0;
85 for (
const auto &str : strings) {
86 if (omitEmpty && str.empty()) {
95 size += (entries * leftClosure.size()) + (entries * rightClosure.size()) + ((entries - 1) * delimiter.size());
97 for (
const auto &str : strings) {
98 if (omitEmpty && str.empty()) {
102 res.append(delimiter);
104 res.append(leftClosure);
106 res.append(rightClosure);
114 template <
class Container = std::initializer_list<std::
string>>
inline std::vector<std::string>
toMultiline(
const Container &arrayOfLines)
137 template <
class Container = std::list<std::
string>>
138 Container
splitString(
const typename Container::value_type &
string,
const typename Container::value_type &delimiter,
139 EmptyPartsTreat emptyPartsRole = EmptyPartsTreat::Keep,
int maxParts = -1)
144 for (
typename Container::value_type::size_type
i = 0, end =
string.size(), delimPos;
i < end;
i = delimPos + delimiter.size()) {
145 delimPos =
string.find(delimiter,
i);
146 if (!merge && maxParts >= 0 && res.size() ==
static_cast<typename Container::value_type::size_type
>(maxParts)) {
153 delimPos = Container::value_type::npos;
155 if (delimPos == Container::value_type::npos) {
156 delimPos =
string.size();
158 if (emptyPartsRole == EmptyPartsTreat::Keep ||
i != delimPos) {
160 res.back().append(delimiter);
161 res.back().append(
string.substr(
i, delimPos -
i));
164 res.emplace_back(
string.substr(
i, delimPos -
i));
184 template <
class Container = std::list<std::
string>>
185 Container
splitStringSimple(
const typename Container::value_type &
string,
const typename Container::value_type &delimiter,
int maxParts = -1)
189 for (
typename Container::value_type::size_type
i = 0, end =
string.size(), delimPos;
i < end;
i = delimPos + delimiter.size()) {
190 delimPos =
string.find(delimiter,
i);
191 if (maxParts >= 0 && res.size() ==
static_cast<typename Container::value_type::size_type
>(maxParts)) {
192 delimPos = Container::value_type::npos;
194 if (delimPos == Container::value_type::npos) {
195 delimPos =
string.size();
197 res.emplace_back(
string.substr(
i, delimPos -
i));
205 template <
class Container = std::vector<std::
string>>
inline std::vector<std::string>
toArrayOfLines(
const std::string &multilineString)
207 return splitString<Container>(multilineString,
"\n", EmptyPartsTreat::Keep);
213 template <
typename StringType>
bool startsWith(
const StringType &str,
const StringType &phrase)
215 if (str.size() < phrase.size()) {
218 for (
auto stri = str.cbegin(), strend = str.cend(), phrasei = phrase.cbegin(), phraseend = phrase.cend();; ++stri, ++phrasei) {
219 if (phrasei == phraseend) {
221 }
else if (stri == strend) {
223 }
else if (*stri != *phrasei) {
233 template <
typename StringType>
bool startsWith(
const StringType &str,
const typename StringType::value_type *phrase)
235 for (
auto stri = str.cbegin(), strend = str.cend();; ++stri, ++phrase) {
238 }
else if (stri == strend) {
240 }
else if (*stri != *phrase) {
250 template <
typename StringType>
bool endsWith(
const StringType &str,
const StringType &phrase)
252 if (str.size() < phrase.size()) {
255 for (
auto stri = str.cend() - phrase.size(), strend = str.cend(), phrasei = phrase.cbegin(); stri != strend; ++stri, ++phrasei) {
256 if (*stri != *phrasei) {
266 template <
typename StringType>
bool endsWith(
const StringType &str,
const typename StringType::value_type *phrase)
268 const auto phraseSize = std::strlen(phrase);
269 if (str.size() < phraseSize) {
272 for (
auto stri = str.cend() - phraseSize, strend = str.cend(); stri != strend; ++stri, ++phrase) {
273 if (*stri != *phrase) {
284 template <
typename StringType>
bool containsSubstrings(
const StringType &str, std::initializer_list<StringType> substrings)
286 typename StringType::size_type currentPos = 0;
287 for (
const auto &substr : substrings) {
288 if ((currentPos = str.find(substr, currentPos)) == StringType::npos) {
291 currentPos += substr.size();
300 template <
typename StringType>
301 bool containsSubstrings(
const StringType &str, std::initializer_list<const typename StringType::value_type *> substrings)
303 typename StringType::size_type currentPos = 0;
304 for (
const auto *substr : substrings) {
305 if ((currentPos = str.find(substr, currentPos)) == StringType::npos) {
308 currentPos += std::strlen(substr);
316 template <
typename StringType1,
typename StringType2,
typename StringType3>
317 void findAndReplace(StringType1 &str,
const StringType2 &find,
const StringType3 &replace)
319 for (
typename StringType1::size_type
i = 0; (
i = str.find(find,
i)) != StringType1::npos;
i += replace.size()) {
320 str.replace(
i, find.size(), replace);
327 template <
typename StringType>
328 inline void findAndReplace(StringType &str,
const typename StringType::value_type *find,
const typename StringType::value_type *replace)
331 str, std::basic_string_view<typename StringType::value_type>(find), std::basic_string_view<typename StringType::value_type>(replace));
337 template <
typename StringType1,
typename StringType2>
338 inline void findAndReplace(StringType1 &str,
const StringType2 &find,
const typename StringType1::value_type *replace)
340 findAndReplace(str, find, std::basic_string_view<typename StringType1::value_type>(replace));
346 template <
typename StringType1,
typename StringType2>
347 inline void findAndReplace(StringType1 &str,
const typename StringType1::value_type *find,
const StringType2 &replace)
349 findAndReplace(str, std::basic_string_view<typename StringType1::value_type>(find), replace);
358 template <
typename CharType> constexpr CharType
digitToChar(CharType digit)
360 return digit <= 9 ? (digit +
'0') : (digit +
'A' - 10);
369 template <
typename IntegralType,
class StringType = std::string,
371 StringType
numberToString(IntegralType number,
typename StringType::value_type base = 10)
373 std::size_t resSize = 0;
374 for (
auto n = number; n; n /= base, ++resSize)
377 res.reserve(resSize);
379 res.insert(res.begin(), digitToChar<typename StringType::value_type>(number % base));
391 template <
typename IntegralType,
class StringType = std::string,
392 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * =
nullptr>
393 StringType
numberToString(IntegralType number,
typename StringType::value_type base = 10)
395 const bool negative = number < 0;
398 number = -number, resSize = 1;
402 for (
auto n = number; n; n /= base, ++resSize)
405 res.reserve(resSize);
407 res.insert(res.begin(), digitToChar<typename StringType::value_type>(number % base));
411 res.insert(res.begin(),
'-');
424 template <
typename FloatingType,
class StringType = std::
string, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
425 StringType
numberToString(FloatingType number,
typename StringType::value_type base = 10)
427 std::basic_stringstream<typename StringType::value_type> ss;
428 ss << std::setbase(base) << number;
437 template <
typename CharType> CharType
charToDigit(CharType character, CharType base)
440 if (character >=
'0' && character <=
'9') {
441 res = character -
'0';
442 }
else if (character >=
'a' && character <=
'z') {
443 res = character -
'a' + 10;
444 }
else if (character >=
'A' && character <=
'Z') {
445 res = character -
'A' + 10;
450 std::string errorMsg;
451 errorMsg.reserve(36);
452 errorMsg +=
"The character \"";
453 errorMsg += character;
454 errorMsg +=
"\" is no valid digit.";
466 template <
typename IntegralType,
typename StringType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
467 IntegralType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
469 IntegralType result = 0;
470 for (
const auto &c :
string) {
475 result += charToDigit<typename StringType::value_type>(c, base);
488 template <
typename IntegralType,
class StringType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
489 IntegralType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
491 auto i =
string.begin();
492 auto end =
string.end();
493 for (;
i != end && *
i ==
' '; ++
i)
498 const bool negative = (*
i ==
'-');
502 IntegralType result = 0;
503 for (;
i != end; ++
i) {
508 result += charToDigit<typename StringType::value_type>(*
i, base);
510 return negative ? -result : result;
523 template <
typename FloatingType,
class StringType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
524 FloatingType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
526 std::basic_stringstream<typename StringType::value_type> ss;
527 ss << std::setbase(base) << string;
529 if ((ss >> result) && ss.eof()) {
532 std::string errorMsg;
533 errorMsg.reserve(42 +
string.size());
534 errorMsg +=
"The string \"";
536 errorMsg +=
"\" is no valid floating number.";
548 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
551 IntegralType result = 0;
552 for (; *string; ++string) {
553 if (*
string ==
' ') {
557 result += charToDigit<CharType>(*
string, base);
572 template <
typename FloatingType,
class CharType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
575 std::basic_stringstream<CharType> ss;
576 ss << std::setbase(base) << string;
578 if ((ss >> result) && ss.eof()) {
581 std::string errorMsg;
582 errorMsg.reserve(42 + std::char_traits<CharType>::length(
string));
583 errorMsg +=
"The string \"";
585 errorMsg +=
"\" is no valid floating number.";
596 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
597 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size,
unsigned char base = 10)
599 IntegralType result = 0;
600 for (
const CharType *end =
string + size;
string != end; ++string) {
601 if (*
string ==
' ') {
605 result += charToDigit<CharType>(*
string, base);
617 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
618 IntegralType
stringToNumber(
const CharType *
string,
unsigned char base = 10)
623 for (; *
string && *
string ==
' '; ++string)
628 const bool negative = (*
string ==
'-');
632 IntegralType result = 0;
633 for (; *string; ++string) {
634 if (*
string ==
' ') {
638 result += charToDigit<CharType>(*
string, base);
640 return negative ? -result : result;
650 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
651 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size,
unsigned char base = 10)
656 const CharType *end =
string + size;
657 for (;
string != end && *
string ==
' '; ++string)
662 const bool negative = (*
string ==
'-');
666 IntegralType result = 0;
667 for (;
string != end; ++string) {
668 if (*
string ==
' ') {
672 result += charToDigit<CharType>(*
string, base);
674 return negative ? -result : result;
688 char buffer[
sizeof(T)];
689 BE::getBytes(integer, buffer);
690 return std::string(buffer + startOffset,
sizeof(T) - startOffset);
699 #endif // CONVERSION_UTILITIES_STRINGCONVERSION_H