diff -r 060a13878ca0 -r be953e1621d9 sources/mystring.cpp --- a/sources/mystring.cpp Wed Jan 27 12:41:50 2021 +0200 +++ b/sources/mystring.cpp Wed Jan 27 19:48:41 2021 +0200 @@ -1,5 +1,5 @@ /* - Copyright 2014 - 2016 Teemu Piippo + Copyright 2014 - 2021 Teemu Piippo All rights reserved. Redistribution and use in source and binary forms, with or without @@ -31,66 +31,17 @@ #include #include "main.h" #include "mystring.h" -#include "md5.h" BEGIN_ZFC_NAMESPACE /*! - * \brief Compares this string with another. - * \param other The string to compare with. - * \returns -1 if this string is lexicographically less than \c other, - * 0 if they are equal, or - * 1 if this string is lexicographically greater than \c other. - */ -int String::compare (const String& other) const -{ - return m_string.compare (other.stdString()); -} - -/*! - * \brief Removes all instances of an unwanted character from this string. - * \param unwanted Character to remove. - */ -void String::strip (char unwanted) -{ - for (int pos = 0; (pos = find (unwanted)) != -1;) - removeAt (pos--); -} - -/*! - * \brief Removes all instances of multiple characters from this string. - * \param unwanted Characters to remove. - */ -void String::strip (const List& unwanted) -{ - for (char character : unwanted) - strip(character); -} - -/*! - * \returns an upper-case version of this string. - */ -String String::toUpperCase() const -{ - String result (m_string); - - for (char &ch : result) - { - if (islower(ch)) - ch -= 'a' - 'A'; - } - - return result; -} - -/*! * \returns a lower-case version of this string. */ -String String::toLowerCase() const +std::string to_lowercase(const std::string& string) { - String result (m_string); + std::string result = string; - for (char &ch : result) + for (char& ch : result) { if (isupper(ch)) ch += 'a' - 'A'; @@ -100,74 +51,35 @@ } /*! - * \brief Splits this string using the provided delimeter. - * \param delimeter Delimeter to use for splitting. - * \returns a string list containing the split strings. + * \brief Joins the elements of this string list into one longer string. + * \param delimeter The delimeter to place between the element strings. + * \returns the catenated string. */ -StringList String::split (char delimeter) const -{ - String delimeterString; - delimeterString += delimeter; - return split (delimeterString); -} - -/*! - * \brief Splits this string using the provided delimeter. - * \param delimeter Delimeter to use for splitting. - * \returns a string list containing the split strings. - */ -StringList String::split (const String& delimeter) const +std::string join_string_list(const std::vector& strings, const std::string& delimeter) { - StringList result; - int a = 0; - int b; - - // Find all separators and store the text left to them. - while ((b = find (delimeter, a)) != -1) - { - String sub = mid (a, b); + std::string result; - if (sub.length() > 0) - result << sub; + for (const std::string &item : strings) + { + if (not result.empty()) + result += delimeter; - a = b + delimeter.length(); + result += item; } - // Add the string at the right of the last separator - if (a < (int) length()) - result.append (mid (a, length())); - return result; } /*! - * \brief Replaces all instances of \c text with \c replacement. - * \param text Text to replace away. - * \param replacement Text to replace \c text with. + * \brief Modifies the given index so that if it is negative, it is translated into a positive index starting from the + * end of the string. For example, an index of -1 will be modified to point to the last character in the string, + * -2 to the second last, etc. + * \param index Index to translate. */ -void String::replace (const char* text, const char* replacement) +inline void modifyIndex(const std::string& str, int& index) { - int position; - - while ((position = find (text)) != -1) - m_string = m_string.replace (position, strlen (text), replacement); -} - -/*! - * \param character Character to count. - * \returns the amount of \c character found in the string. - */ -int String::count (char character) const -{ - int result = 0; - - for (char ch : *this) - { - if (ch == character) - result++; - } - - return result; + if (index < 0) + index = str.length() - index; } /*! @@ -175,204 +87,29 @@ * \param b Ending index of the range. * \returns a sub-string containing all characters from \c a to \c b, not including the character at \c b. */ -String String::mid (int rangeBegin, int rangeEnd) const +std::string mid(const std::string& str, int rangeBegin, int rangeEnd) { - modifyIndex(rangeBegin); - modifyIndex(rangeEnd); + modifyIndex(str, rangeBegin); + modifyIndex(str, rangeEnd); rangeBegin = max(rangeBegin, 0); - rangeEnd = min(rangeEnd, length()); + rangeEnd = min(rangeEnd, static_cast(str.length())); if (rangeEnd <= rangeBegin) return ""; else - return m_string.substr(rangeBegin, rangeEnd - rangeBegin); + return str.substr(rangeBegin, rangeEnd - rangeBegin); } /*! * \param length Amount of characters to return. * \returns the \c length right-most characters of the string. */ -String String::right(int length) const -{ - if (length >= this->length()) - return *this; - else - return String(chars() + this->length() - length); -} - -/*! - * \brief Finds the first instance of a sub-string. - * \param subString Sub-string to search within this string. - * \param startingPosition Position to start looking for the sub-string from. - * \returns the position the first instance of sub-string found, or -1 if not found. - */ -int String::find (const char* subString, int startingPosition) const -{ - int position = m_string.find (subString, startingPosition); - - if (position == int (std::string::npos)) - return -1; - else - return position; -} - -/*! - * \brief Finds the first instance of a character. - * \param character Character to search within this string. - * \param startingPosition Position to start looking for the character from. - * \returns the position of the first instance of the provided character found, or -1 if not found. - */ -int String::find (char character, int startingPosition) const -{ - int position = m_string.find (character, startingPosition); - - if (position == int (std::string::npos)) - return -1; - else - return position; -} - -/*! - * \brief Finds the last instance of a sub-string. - * \param subString Sub-string to search within this string. - * \param startingPosition Position to start looking for the sub-string from. - * \returns the position the last instance of sub-string found, or -1 if not found. - */ -int String::findLast (const char* subString, int startingPosition) const -{ - modifyIndex(startingPosition); - - for (; startingPosition > 0; startingPosition--) - { - if (strncmp (chars() + startingPosition, subString, strlen (subString)) == 0) - return startingPosition; - } - - return -1; -} - -/*! - * \brief Converts this string to an integer. - * \param ok An pointer to a boolean to store whether or not the conversion was successful. - * If \c ok is \c NULL, the success state is not stored. - * \param base The base to interpret this string with. - * \returns the resulting integer. - */ -long String::toInt (bool* ok, int base) const -{ - errno = 0; - char* endPointer; - long result = strtol (chars(), &endPointer, base); - - if (ok != nullptr) - *ok = (errno == 0 and *endPointer == '\0'); - - return result; -} - -/*! - * \brief Converts this string to a floating-point number. - * \param ok An pointer to a boolean to store whether or not the conversion was successful. - * If \c ok is \c NULL, the success state is not stored. - * \returns the resulting floating-point number. - */ -float String::toFloat (bool* ok) const +std::string right(const std::string& str, int length) { - return static_cast(toDouble(ok)); -} - -/*! - * \brief Converts this string to a double-precision floating-point number. - * \param ok An pointer to a boolean to store whether or not the conversion was successful. - * If \c ok is \c NULL, the success state is not stored. - * \returns the resulting floating-point number. - */ -double String::toDouble (bool* ok) const -{ - errno = 0; - char* endptr; - double i = strtod (chars(), &endptr); - - if (ok != nullptr) - *ok = (errno == 0 and *endptr == '\0'); - - return i; -} - -/*! - * \brief Catenates this string with another string. - * \param text String to catenate to the end of this string. - * \returns the resulting string. - */ -String String::operator+ (const String& text) const -{ - String newString = *this; - newString.append (text); - return newString; -} - -/*! - * \brief Catenates this string with another string. - * \param text String to catenate to the end of this string. - * \returns the resulting string. - */ -String String::operator+ (const char* text) const -{ - String newString = *this; - newString.append (text); - return newString; -} - -/*! - * \returns whether or not this string represents a number. - */ -bool String::isNumeric() const -{ - char* endPointer; - strtol (chars(), &endPointer, 10); - return (endPointer != nullptr) and (*endPointer != '\0'); -} - -/*! - * \param other Sub-string to find from the end of this string. - * \return whether or not this string ends with the provided sub-string. - */ -bool String::endsWith (const String& other) const -{ - if (length() < other.length()) - { - return false; - } + if (length >= static_cast(str.length())) + return str; else - { - const int offset = length() - other.length(); - return strncmp (chars() + offset, other.chars(), other.length()) == 0; - } -} - -/*! - * \param other Sub-string to find from the beginning of this string. - * \returns whether or not this string begins with the provided sub-string. - */ -bool String::startsWith (const String& other) const -{ - if (length() < other.length()) - return false; - else - return strncmp (chars(), other.chars(), other.length()) == 0; -} - -/*! - * \brief Formats this string using \c printf -like syntax. - * \param formatString Template string to use with formatting. - * \param ... Variadic arguments to use with formatting. - */ -void __cdecl String::sprintf (const char* formatString, ...) -{ - va_list args; - va_start (args, formatString); - this->vsprintf (formatString, args); - va_end (args); + return std::string{str.data() + str.length() - length}; } /*! @@ -380,8 +117,10 @@ * \param formatString Template string to use with formatting. * \param args Variadic arguments to use with formatting. */ -void String::vsprintf (const char* formatString, va_list args) +std::string vsprintf(const char* formatString, va_list args) { + std::string result; + // Copy the argument list so that we have something to provide to vsnprintf in case we have to call it again. va_list argsCopy; va_copy(argsCopy, args); @@ -393,192 +132,118 @@ if (length < sizeof buffer) { // vsnprintf succeeded in fitting the formatted string into the buffer, so we're done. - m_string = buffer; + result = buffer; } else { // vsnprintf needs more space, so we have to allocate a new buffer and try again. - Vector newBuffer(length + 1); + std::vector newBuffer(length + 1); vsnprintf(newBuffer.data(), length + 1, formatString, argsCopy); - m_string = newBuffer; + result = newBuffer.data(); + } + + return result; +} + +/*! + * \brief Formats this string using \c printf -like syntax. + * \param formatString Template string to use with formatting. + * \param ... Variadic arguments to use with formatting. + */ +std::string __cdecl sprintf(const char* formatString, ...) +{ + va_list args; + va_start (args, formatString); + std::string result = vsprintf(formatString, args); + va_end (args); + return result; +} + +std::string remove_range(const std::string &string, int start, int end) +{ + std::string result; + result.reserve(string.length() - (end - start)); + std::copy(string.begin(), string.begin() + start, std::back_inserter(result)); + std::copy(string.begin() + end, string.end(), std::back_inserter(result)); + return result; +} + + +/*! + * \param other Sub-string to find from the beginning of this string. + * \returns whether or not this string begins with the provided sub-string. + */ +bool starts_with(const std::string& str, const std::string& other) +{ + if (str.length() < other.length()) + return false; + else + return std::strncmp(str.data(), other.data(), other.length()) == 0; +} + +/*! + * \brief Replaces all instances of \c text with \c replacement. + * \param text Text to replace away. + * \param replacement Text to replace \c text with. + */ +void replace_all(std::string& str, const char* text, const char* replacement) +{ + int position; + while ((position = str.find(text)) != -1) + { + str.replace(position, std::strlen(text), replacement); } } /*! - * \brief Joins the elements of this string list into one longer string. - * \param delimeter The delimeter to place between the element strings. - * \returns the catenated string. + * \brief Splits this string using the provided delimeter. + * \param delimeter Delimeter to use for splitting. + * \returns a string list containing the split strings. */ -String StringList::join (const String& delimeter) +std::vector split(const std::string& string, const std::string& delimeter) { - String result; + std::vector result; + int a = 0; + int b; - for (const String &item : container()) + // Find all separators and store the text left to them. + while ((b = string.find(delimeter, a)) != -1) { - if (result.isEmpty() == false) - result += delimeter; + std::string sub = mid(string, a, b); + + if (sub.length() > 0) + result.push_back(sub); - result += item; + a = b + delimeter.length(); } + // Add the string at the right of the last separator + if (a < static_cast(string.length())) + result.push_back(mid(string, a, string.length())); + return result; } /*! - * \brief Tries to match this string against a mask pattern. In the pattern, '?' refers to one character, and '*' to - * any number of characters. - * \param pattern The masking pattern to use for matching. - * \returns whether or not this string matches the provided pattern. + * \brief Converts this string to an integer. + * \param ok An pointer to a boolean to store whether or not the conversion was successful. + * If \c ok is \c NULL, the success state is not stored. + * \param base The base to interpret this string with. + * \returns the resulting integer. */ -bool String::maskAgainst (const String& pattern) const -{ - // Elevate to uppercase for case-insensitive matching - String pattern_upper = pattern.toUpperCase(); - String this_upper = toUpperCase(); - const char* maskstring = pattern_upper.chars(); - const char* mptr = &maskstring[0]; - - for (const char* sptr = this_upper.chars(); *sptr != '\0'; sptr++) - { - if (*mptr == '?') - { - if (*(sptr + 1) == '\0') - { - // ? demands that there's a character here and there wasn't. - // Therefore, mask matching fails - return false; - } - } - else if (*mptr == '*') - { - char end = *(++mptr); - - // If '*' is the final character of the message, all of the remaining - // string matches against the '*'. We don't need to bother checking - // the string any further. - if (end == '\0') - return true; - - // Skip to the end character - while (*sptr != end and *sptr != '\0') - sptr++; - - // String ended while the mask still had stuff - if (*sptr == '\0') - return false; - } - else if (*sptr != *mptr) - return false; - - mptr++; - } - - return true; -} - -/*! - * \brief Converts a short integer into a string. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (short int value) -{ - char buffer[32]; - ::sprintf (buffer, "%d", value); - return String (buffer); -} - -/*! - * \brief Converts an integer into a string. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (int value) +std::optional to_int(const char* str, int base) { - char buffer[32]; - ::sprintf (buffer, "%d", value); - return String (buffer); -} - -/*! - * \brief Converts a long integer into a string. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (long int value) -{ - char buffer[32]; - ::sprintf (buffer, "%ld", value); - return String (buffer); -} - -/*! - * \brief Converts an unsigned short integer into a string. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (unsigned short int value) -{ - char buffer[32]; - ::sprintf (buffer, "%u", value); - return String (buffer); -} - -/*! - * \brief Converts an unsigned integer into a string. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (unsigned int value) -{ - char buffer[32]; - ::sprintf (buffer, "%u", value); - return String (buffer); -} - -/*! - * \brief Converts an unsigned long integer into a string. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (unsigned long int value) -{ - char buffer[32]; - ::sprintf (buffer, "%lu", value); - return String (buffer); -} - -/*! - * \brief Converts a double-precision floating point number into a string, using the "%f" format specifier. - * \param value The value to convert. - * \returns the resulting string. - */ -String String::fromNumber (double value) -{ - char buffer[64]; - ::sprintf (buffer, "%f", value); - return String (buffer); -} - -/*! - * \brief Constructs a string from a vector of bytes. The bytes do not have to be null-terminated. - * \param bytes Bytes to use for construction - * \returns the resulting string. - */ -String String::fromBytes(const ByteArray& bytes) -{ - return String(reinterpret_cast&>(bytes)); -} - -/*! - * \returns the MD5-checksum of this string. - */ -String String::md5() const -{ - char checksum[33]; - CalculateMD5 (reinterpret_cast (chars()), length(), checksum); - checksum[sizeof checksum - 1] = '\0'; - return String (checksum); + errno = 0; + char* endPointer; + long result = strtol(str, &endPointer, base); + if (errno == 0 and *endPointer == '\0') + { + return result; + } + else + { + return {}; + } } /*! @@ -586,32 +251,26 @@ * something else than whitespace. * \param filter The filtering function to use. */ -void String::normalize (int (*filter)(int)) +void normalize(std::string& string, int (*filter)(int)) { int a = 0; - int b = length() - 1; - - while ((*filter) (m_string[a]) and a != b) + int b = string.length() - 1; + while ((*filter)(string[a]) and a != b) + { ++a; - - while ((*filter) (m_string[b]) and a != b) + } + while ((*filter)(string[b]) and a != b) + { --b; - + } if (a == b) - m_string = ""; - else if (a != 0 or b != length() - 1) - m_string = m_string.substr (a, b - a + 1); -} - -/*! - * \returns a version of this string without leading or trailing whitespace. Alternatively a custom filter can be used - * to strip something else than whitespace. - */ -String String::normalized (int (*filter)(int)) const -{ - String result = *this; - result.normalize(filter); - return result; + { + string = ""; + } + else if (a != 0 or b != static_cast(string.length() - 1)) + { + string = string.substr (a, b - a + 1); + } } END_ZFC_NAMESPACE