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>,
class ReturnType =
typename Container::value_type>
75 ReturnType
joinStrings(
const Container &strings,
const typename Container::value_type &delimiter =
typename Container::value_type(),
76 bool omitEmpty =
false,
const typename Container::value_type &leftClosure =
typename Container::value_type(),
77 const typename Container::value_type &rightClosure =
typename Container::value_type())
80 if (!strings.size()) {
83 std::size_t entries = 0, size = 0;
84 for (
const auto &str : strings) {
85 if (omitEmpty && str.empty()) {
94 size += (entries * leftClosure.size()) + (entries * rightClosure.size()) + ((entries - 1) * delimiter.size());
96 for (
const auto &str : strings) {
97 if (omitEmpty && str.empty()) {
101 res.append(delimiter);
103 res.append(leftClosure);
105 res.append(rightClosure);
113 template <
class Container = std::initializer_list<std::
string>>
inline std::vector<std::string>
toMultiline(
const Container &arrayOfLines)
136 template <
class Container = std::list<std::
string>>
137 Container
splitString(
const typename Container::value_type &
string,
const typename Container::value_type &delimiter,
143 for (
typename Container::value_type::size_type
i = 0, end =
string.size(), delimPos;
i < end;
i = delimPos + delimiter.size()) {
144 delimPos =
string.find(delimiter,
i);
145 if (!merge && maxParts >= 0 && res.size() ==
static_cast<typename Container::value_type::size_type
>(maxParts)) {
152 delimPos = Container::value_type::npos;
154 if (delimPos == Container::value_type::npos) {
155 delimPos =
string.size();
159 res.back().append(delimiter);
160 res.back().append(
string.substr(
i, delimPos -
i));
163 res.emplace_back(
string.substr(
i, delimPos -
i));
183 template <
class Container = std::list<std::
string>>
184 Container
splitStringSimple(
const typename Container::value_type &
string,
const typename Container::value_type &delimiter,
int maxParts = -1)
188 for (
typename Container::value_type::size_type
i = 0, end =
string.size(), delimPos;
i < end;
i = delimPos + delimiter.size()) {
189 delimPos =
string.find(delimiter,
i);
190 if (maxParts >= 0 && res.size() ==
static_cast<typename Container::value_type::size_type
>(maxParts)) {
191 delimPos = Container::value_type::npos;
193 if (delimPos == Container::value_type::npos) {
194 delimPos =
string.size();
196 res.emplace_back(
string.substr(
i, delimPos -
i));
204 template <
class Container = std::vector<std::
string>>
inline std::vector<std::string>
toArrayOfLines(
const std::string &multilineString)
212 template <
typename StringType>
bool startsWith(
const StringType &str,
const StringType &phrase)
214 if (str.size() < phrase.size()) {
217 for (
auto stri = str.cbegin(), strend = str.cend(), phrasei = phrase.cbegin(), phraseend = phrase.cend();; ++stri, ++phrasei) {
218 if (phrasei == phraseend) {
220 }
else if (stri == strend) {
222 }
else if (*stri != *phrasei) {
232 template <
typename StringType>
bool startsWith(
const StringType &str,
const typename StringType::value_type *phrase)
234 for (
auto stri = str.cbegin(), strend = str.cend();; ++stri, ++phrase) {
237 }
else if (stri == strend) {
239 }
else if (*stri != *phrase) {
249 template <
typename StringType>
bool endsWith(
const StringType &str,
const StringType &phrase)
251 if (str.size() < phrase.size()) {
254 for (
auto stri = str.cend() - phrase.size(), strend = str.cend(), phrasei = phrase.cbegin(); stri != strend; ++stri, ++phrasei) {
255 if (*stri != *phrasei) {
265 template <
typename StringType>
bool endsWith(
const StringType &str,
const typename StringType::value_type *phrase)
267 const auto phraseSize = std::strlen(phrase);
268 if (str.size() < phraseSize) {
271 for (
auto stri = str.cend() - phraseSize, strend = str.cend(); stri != strend; ++stri, ++phrase) {
272 if (*stri != *phrase) {
283 template <
typename StringType>
bool containsSubstrings(
const StringType &str, std::initializer_list<StringType> substrings)
285 typename StringType::size_type currentPos = 0;
286 for (
const auto &substr : substrings) {
287 if ((currentPos = str.find(substr, currentPos)) == StringType::npos) {
290 currentPos += substr.size();
299 template <
typename StringType>
300 bool containsSubstrings(
const StringType &str, std::initializer_list<const typename StringType::value_type *> substrings)
302 typename StringType::size_type currentPos = 0;
303 for (
const auto *substr : substrings) {
304 if ((currentPos = str.find(substr, currentPos)) == StringType::npos) {
307 currentPos += std::strlen(substr);
315 template <
typename StringType>
void findAndReplace(StringType &str,
const StringType &find,
const StringType &replace)
317 for (
typename StringType::size_type
i = 0; (
i = str.find(find,
i)) != StringType::npos;
i += replace.size()) {
318 str.replace(
i, find.size(), replace);
328 template <
typename CharType> constexpr CharType
digitToChar(CharType digit)
330 return digit <= 9 ? (digit +
'0') : (digit +
'A' - 10);
339 template <
typename IntegralType,
class StringType = std::string,
341 StringType
numberToString(IntegralType number,
typename StringType::value_type base = 10)
343 std::size_t resSize = 0;
344 for (
auto n = number; n; n /= base, ++resSize)
347 res.reserve(resSize);
349 res.insert(res.begin(), digitToChar<typename StringType::value_type>(number % base));
361 template <
typename IntegralType,
class StringType = std::string,
362 Traits::EnableIf<std::is_integral<IntegralType>, std::is_signed<IntegralType>> * =
nullptr>
363 StringType
numberToString(IntegralType number,
typename StringType::value_type base = 10)
365 const bool negative = number < 0;
368 number = -number, resSize = 1;
372 for (
auto n = number; n; n /= base, ++resSize)
375 res.reserve(resSize);
377 res.insert(res.begin(), digitToChar<typename StringType::value_type>(number % base));
381 res.insert(res.begin(),
'-');
394 template <
typename FloatingType,
class StringType = std::
string, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
395 StringType
numberToString(FloatingType number,
typename StringType::value_type base = 10)
397 std::basic_stringstream<typename StringType::value_type> ss;
398 ss << std::setbase(base) << number;
407 template <
typename CharType> CharType
charToDigit(CharType character, CharType base)
410 if (character >=
'0' && character <=
'9') {
411 res = character -
'0';
412 }
else if (character >=
'a' && character <=
'z') {
413 res = character -
'a' + 10;
414 }
else if (character >=
'A' && character <=
'Z') {
415 res = character -
'A' + 10;
420 std::string errorMsg;
421 errorMsg.reserve(36);
422 errorMsg +=
"The character \"";
423 errorMsg += character;
424 errorMsg +=
"\" is no valid digit.";
436 template <
typename IntegralType,
typename StringType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
437 IntegralType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
439 IntegralType result = 0;
440 for (
const auto &c :
string) {
445 result += charToDigit<typename StringType::value_type>(c, base);
458 template <
typename IntegralType,
class StringType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
459 IntegralType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
461 auto i =
string.begin();
462 auto end =
string.end();
463 for (;
i != end && *
i ==
' '; ++
i)
468 const bool negative = (*
i ==
'-');
472 IntegralType result = 0;
473 for (;
i != end; ++
i) {
478 result += charToDigit<typename StringType::value_type>(*
i, base);
480 return negative ? -result : result;
493 template <
typename FloatingType,
class StringType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
494 FloatingType
stringToNumber(
const StringType &
string,
typename StringType::value_type base = 10)
496 std::basic_stringstream<typename StringType::value_type> ss;
497 ss << std::setbase(base) << string;
499 if ((ss >> result) && ss.eof()) {
502 std::string errorMsg;
503 errorMsg.reserve(42 +
string.size());
504 errorMsg +=
"The string \"";
506 errorMsg +=
"\" is no valid floating number.";
518 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
521 IntegralType result = 0;
522 for (; *string; ++string) {
523 if (*
string ==
' ') {
527 result += charToDigit<CharType>(*
string, base);
542 template <
typename FloatingType,
class CharType, Traits::EnableIf<std::is_
floating_po
int<FloatingType>> * =
nullptr>
545 std::basic_stringstream<CharType> ss;
546 ss << std::setbase(base) << string;
548 if ((ss >> result) && ss.eof()) {
551 std::string errorMsg;
552 errorMsg.reserve(42 + std::char_traits<CharType>::length(
string));
553 errorMsg +=
"The string \"";
555 errorMsg +=
"\" is no valid floating number.";
566 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
unsigned<IntegralType>> * =
nullptr>
567 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size,
unsigned char base = 10)
569 IntegralType result = 0;
570 for (
const CharType *end =
string + size;
string != end; ++string) {
571 if (*
string ==
' ') {
575 result += charToDigit<CharType>(*
string, base);
587 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
588 IntegralType
stringToNumber(
const CharType *
string,
unsigned char base = 10)
593 for (; *
string && *
string ==
' '; ++string)
598 const bool negative = (*
string ==
'-');
602 IntegralType result = 0;
603 for (; *string; ++string) {
604 if (*
string ==
' ') {
608 result += charToDigit<CharType>(*
string, base);
610 return negative ? -result : result;
620 template <
typename IntegralType,
class CharType, Traits::EnableIf<std::is_
integral<IntegralType>, std::is_
signed<IntegralType>> * =
nullptr>
621 IntegralType
bufferToNumber(
const CharType *
string, std::size_t size,
unsigned char base = 10)
626 const CharType *end =
string + size;
627 for (;
string != end && *
string ==
' '; ++string)
632 const bool negative = (*
string ==
'-');
636 IntegralType result = 0;
637 for (;
string != end; ++string) {
638 if (*
string ==
' ') {
642 result += charToDigit<CharType>(*
string, base);
644 return negative ? -result : result;
658 char buffer[
sizeof(T)];
659 BE::getBytes(integer, buffer);
660 return std::string(buffer + startOffset,
sizeof(T) - startOffset);
669 #endif // CONVERSION_UTILITIES_STRINGCONVERSION_H