1 #ifndef CONVERSION_UTILITIES_STRINGCONVERSION_H
2 #define CONVERSION_UTILITIES_STRINGCONVERSION_H
7 #include "../misc/traits.h"
11 #include <initializer_list>
17 #include <system_error>
32 std::free(stringData);
39 using StringData = std::pair<std::unique_ptr<char[], StringDataDeleter>, std::size_t>;
43 const char *fromCharset,
const char *toCharset,
const char *inputBuffer, std::size_t inputBufferSize,
float outputBufferSizeFactor = 1.0f);
51 #ifdef PLATFORM_WINDOWS
52 using WideStringData = std::pair<std::unique_ptr<wchar_t[]>,
int>;
53 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec,
const char *inputBuffer,
int inputBufferSize = -1);
54 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(std::error_code &ec,
const std::string &inputBuffer);
55 CPP_UTILITIES_EXPORT WideStringData convertMultiByteToWide(
const char *inputBuffer,
int inputBufferSize = -1);
74 template <
class Container = std::initializer_list<std::
string>>
75 typename Container::value_type
joinStrings(
const Container &strings,
76 const typename Container::value_type &delimiter =
typename Container::value_type(),
bool omitEmpty =
false,
77 const typename Container::value_type &leftClosure =
typename Container::value_type(),
78 const typename Container::value_type &rightClosure =
typename Container::value_type())
80 typename Container::value_type res;
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,
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();
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)
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 StringType>
void findAndReplace(StringType &str,
const StringType &find,
const StringType &replace)
318 for (
typename StringType::size_type
i = 0; (
i = str.find(find,
i)) != StringType::npos;
i += replace.size()) {
319 str.replace(
i, find.size(), replace);
329 template <
typename CharType> constexpr CharType
digitToChar(CharType digit)
331 return digit <= 9 ? (digit +
'0') : (digit +
'A' - 10);
340 template <
typename IntegralType,
class StringType = std::string,
342 StringType
numberToString(IntegralType number,
typename StringType::value_type base = 10)
344 std::size_t resSize = 0;
345 for (
auto n = number; n; n /= base, ++resSize)
348 res.reserve(resSize);
350 res.insert(res.begin(), digitToChar<typename StringType::value_type>(number % base));
362 template <
typename IntegralType,
class StringType = std::string,
363 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * =
nullptr>
364 StringType
numberToString(IntegralType number,
typename StringType::value_type base = 10)
366 const bool negative = number < 0;
369 number = -number, resSize = 1;
373 for (
auto n = number; n; n /= base, ++resSize)
376 res.reserve(resSize);
378 res.insert(res.begin(), digitToChar<typename StringType::value_type>(number % base));
382 res.insert(res.begin(),
'-');
395 template <
typename FloatingType,
class StringType = std::
string, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
396 StringType
numberToString(FloatingType number,
typename StringType::value_type base = 10)
398 std::basic_stringstream<typename StringType::value_type> ss;
399 ss << std::setbase(base) << number;
408 template <
typename CharType> CharType
charToDigit(CharType character, CharType base)
411 if (character >=
'0' && character <=
'9') {
412 res = character -
'0';
413 }
else if (character >=
'a' && character <=
'z') {
414 res = character -
'a' + 10;
415 }
else if (character >=
'A' && character <=
'Z') {
416 res = character -
'A' + 10;
421 std::string errorMsg;
422 errorMsg.reserve(36);
423 errorMsg +=
"The character \"";
424 errorMsg += character;
425 errorMsg +=
"\" is no valid digit.";
437 template <
typename IntegralType,
typename StringType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
438 IntegralType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
440 IntegralType result = 0;
441 for (
const auto &c :
string) {
446 result += charToDigit<typename StringType::value_type>(c, base);
459 template <
typename IntegralType,
class StringType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
460 IntegralType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
462 auto i =
string.begin();
463 auto end =
string.end();
464 for (;
i != end && *
i ==
' '; ++
i)
469 const bool negative = (*
i ==
'-');
473 IntegralType result = 0;
474 for (;
i != end; ++
i) {
479 result += charToDigit<typename StringType::value_type>(*
i, base);
481 return negative ? -result : result;
494 template <
typename FloatingType,
class StringType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
495 FloatingType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
497 std::basic_stringstream<typename StringType::value_type> ss;
498 ss << std::setbase(base) << string;
500 if ((ss >> result) && ss.eof()) {
503 std::string errorMsg;
504 errorMsg.reserve(42 +
string.size());
505 errorMsg +=
"The string \"";
507 errorMsg +=
"\" is no valid floating number.";
519 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
522 IntegralType result = 0;
523 for (; *string; ++string) {
524 if (*
string ==
' ') {
528 result += charToDigit<CharType>(*
string, base);
543 template <
typename FloatingType,
class CharType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
546 std::basic_stringstream<CharType> ss;
547 ss << std::setbase(base) << string;
549 if ((ss >> result) && ss.eof()) {
552 std::string errorMsg;
553 errorMsg.reserve(42 + std::char_traits<CharType>::length(
string));
554 errorMsg +=
"The string \"";
556 errorMsg +=
"\" is no valid floating number.";
567 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
568 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size,
unsigned char base = 10)
570 IntegralType result = 0;
571 for (
const CharType *end =
string + size;
string != end; ++string) {
572 if (*
string ==
' ') {
576 result += charToDigit<CharType>(*
string, base);
588 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
589 IntegralType
stringToNumber(
const CharType *
string,
unsigned char base = 10)
594 for (; *
string && *
string ==
' '; ++string)
599 const bool negative = (*
string ==
'-');
603 IntegralType result = 0;
604 for (; *string; ++string) {
605 if (*
string ==
' ') {
609 result += charToDigit<CharType>(*
string, base);
611 return negative ? -result : result;
621 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
622 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size,
unsigned char base = 10)
627 const CharType *end =
string + size;
628 for (;
string != end && *
string ==
' '; ++string)
633 const bool negative = (*
string ==
'-');
637 IntegralType result = 0;
638 for (;
string != end; ++string) {
639 if (*
string ==
' ') {
643 result += charToDigit<CharType>(*
string, base);
645 return negative ? -result : result;
659 char buffer[
sizeof(T)];
660 BE::getBytes(integer, buffer);
661 return std::string(buffer + startOffset,
sizeof(T) - startOffset);
670 #endif // CONVERSION_UTILITIES_STRINGCONVERSION_H