--- a/sources/mystring.cpp Wed Jul 20 15:03:37 2016 +0300 +++ b/sources/mystring.cpp Wed Jul 20 17:56:40 2016 +0300 @@ -35,51 +35,42 @@ 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.std_string()); -} - -// ------------------------------------------------------------------------------------------------- -// -void String::trim (int n) -{ - if (n > 0) - m_string = mid (0, length() - n).std_string(); - else - m_string = mid (n, -1).std_string(); + return m_string.compare (other.stdString()); } -// ------------------------------------------------------------------------------------------------- -// -String String::strip (char unwanted) const +/*! + * \brief Removes all instances of an unwanted character from this string. + * \param unwanted Character to remove. + */ +void String::strip (char unwanted) { - String result (m_string); - - for (int pos = 0; (pos = result.find (unwanted)) != -1;) - result.remove_at (pos--); - - return result; + for (int pos = 0; (pos = find (unwanted)) != -1;) + removeAt (pos--); } -// ------------------------------------------------------------------------------------------------- -// -String String::strip (const List<char>& unwanted) const +/*! + * \brief Removes all instances of multiple characters from this string. + * \param unwanted Characters to remove. + */ +void String::strip (const List<char>& unwanted) { - String result (m_string); - - for (String c : unwanted) - for (int pos = 0; (pos = result.find (c)) != -1;) - result.remove_at (pos--); - - return result; + for (char character : unwanted) + strip(character); } -// ------------------------------------------------------------------------------------------------- -// -String String::to_uppercase() const +/*! + * \returns an upper-case version of this string. + */ +String String::toUpperCase() const { String result (m_string); @@ -92,9 +83,10 @@ return result; } -// ------------------------------------------------------------------------------------------------- -// -String String::to_lowercase() const +/*! + * \returns a lower-case version of this string. + */ +String String::toLowerCase() const { String result (m_string); @@ -107,86 +99,99 @@ return result; } -// ------------------------------------------------------------------------------------------------- -// -StringList String::split (char del) const +/*! + * \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 (char delimeter) const { - String delimstr; - delimstr += del; - return split (delimstr); + String delimeterString; + delimeterString += delimeter; + return split (delimeterString); } -// ------------------------------------------------------------------------------------------------- -// -StringList String::split (const String& del) const +/*! + * \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 { - StringList res; + StringList result; int a = 0; int b; // Find all separators and store the text left to them. - while ((b = find (del, a)) != -1) + while ((b = find (delimeter, a)) != -1) { String sub = mid (a, b); if (sub.length() > 0) - res << sub; + result << sub; - a = b + del.length(); + a = b + delimeter.length(); } // Add the string at the right of the last separator if (a < (int) length()) - res.append (mid (a, length())); + result.append (mid (a, length())); - return res; + return result; } -// ------------------------------------------------------------------------------------------------- -// -void String::replace (const char* a, const char* b) +/*! + * \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 String::replace (const char* text, const char* replacement) { - long pos; + int position; - while ((pos = find (a)) != -1) - m_string = m_string.replace (pos, strlen (a), b); + while ((position = find (text)) != -1) + m_string = m_string.replace (position, strlen (text), replacement); } -// ------------------------------------------------------------------------------------------------- -// -int String::count (char needle) const +/*! + * \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 == needle) + if (ch == character) result++; } return result; } -// ------------------------------------------------------------------------------------------------- -// -// Returns a substring from [a, b) -// -String String::mid (int a, int b) const +/*! + * \param a Starting index of the range. + * \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 { - a = max(a, 0); - b = min(b, length()); + modifyIndex(rangeBegin); + modifyIndex(rangeEnd); + rangeBegin = max(rangeBegin, 0); + rangeEnd = min(rangeEnd, length()); - if (b == -1) - b = length(); - - if (b <= a) + if (rangeEnd <= rangeBegin) return ""; else - return m_string.substr(a, b - a); + return m_string.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()) @@ -195,90 +200,94 @@ return String(chars() + this->length() - length); } -// ------------------------------------------------------------------------------------------------- -// -int String::word_position (int n) const +/*! + * \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 count = 0; + 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); - for (char ch : *this) + 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 (not isspace(ch) or ++count < n) - continue; + if (strncmp (chars() + startingPosition, subString, strlen (subString)) == 0) + return startingPosition; } return -1; } -// ------------------------------------------------------------------------------------------------- -// -int String::find (const char* c, int a) const +/*! + * \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 { - int pos = m_string.find (c, a); - - if (pos == int (std::string::npos)) - return -1; + errno = 0; + char* endPointer; + long result = strtol (chars(), &endPointer, base); - return pos; -} + if (ok != nullptr) + *ok = (errno == 0 and *endPointer == '\0'); -// ------------------------------------------------------------------------------------------------- -// -int String::find (char ch, int a) const -{ - int pos = m_string.find (ch, a); - - if (pos == int (std::string::npos)) - return -1; - - return pos; + return result; } -// ------------------------------------------------------------------------------------------------- -// -int String::find_last (const char* c, int a) const +/*! + * \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 { - if (a == -1 or a >= length()) - a = length() - 1; - - for (; a > 0; a--) - if (m_string[a] == c[0] and strncmp (chars() + a, c, strlen (c)) == 0) - return a; - - return -1; + return static_cast<float>(toDouble(ok)); } -// ------------------------------------------------------------------------------------------------- -// -long String::to_int (bool* ok, int base) const -{ - errno = 0; - char* endptr; - long i = strtol (chars(), &endptr, base); - - if (ok) - *ok = (errno == 0 and *endptr == '\0'); - - return i; -} - -// ------------------------------------------------------------------------------------------------- -// -float String::to_float (bool* ok) const -{ - errno = 0; - char* endptr; - float i = (float) strtod (chars(), &endptr); - - if (ok != nullptr) - *ok = (errno == 0 and *endptr == '\0'); - - return i; -} - -// ------------------------------------------------------------------------------------------------- -// -double String::to_double (bool* ok) const +/*! + * \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; @@ -290,93 +299,124 @@ return i; } -// ------------------------------------------------------------------------------------------------- -// -String String::operator+ (const String& data) const +/*! + * \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 (data); + 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; } -// ------------------------------------------------------------------------------------------------- -// -String String::operator+ (const char* data) const +/*! + * \returns whether or not this string represents a number. + */ +bool String::isNumeric() const { - String newstr = *this; - newstr.append (data); - return newstr; + char* endPointer; + strtol (chars(), &endPointer, 10); + return (endPointer != nullptr) and (*endPointer != '\0'); } -// ------------------------------------------------------------------------------------------------- -// -bool String::is_numeric() const +/*! + * \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 { - char* endptr; - strtol (chars(), &endptr, 10); - return not (endptr && *endptr); + if (length() < other.length()) + { + return false; + } + else + { + const int offset = length() - other.length(); + return strncmp (chars() + offset, other.chars(), other.length()) == 0; + } } -// ------------------------------------------------------------------------------------------------- -// -bool String::ends_with (const String& other) +/*! + * \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; - - const int ofs = length() - other.length(); - return strncmp (chars() + ofs, other.chars(), other.length()) == 0; + else + return strncmp (chars(), other.chars(), other.length()) == 0; } -// ------------------------------------------------------------------------------------------------- -// -bool String::starts_with (const String& other) const -{ - if (length() < other.length()) - return false; - - return strncmp (chars(), other.chars(), other.length()) == 0; -} - -// ------------------------------------------------------------------------------------------------- -// -void __cdecl String::sprintf (const char* fmtstr, ...) +/*! + * \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, fmtstr); - this->vsprintf (fmtstr, args); + va_start (args, formatString); + this->vsprintf (formatString, args); va_end (args); } -// ------------------------------------------------------------------------------------------------- -// -void String::vsprintf (const char* fmtstr, va_list args) +/*! + * \brief Formats this string using \c vsnprintf, using the provided arguments. + * \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) { - char* buf = nullptr; - int bufsize = 256; + // 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); - do + // First, attempt to format using a fixed-size buffer. + static char buffer[1024]; + size_t length = vsnprintf(buffer, sizeof buffer, formatString, args); + + if (length < sizeof buffer) { - bufsize *= 2; - delete[] buf; - buf = new char[bufsize]; + // vsnprintf succeeded in fitting the formatted string into the buffer, so we're done. + m_string = buffer; } - while (vsnprintf (buf, bufsize, fmtstr, args) >= bufsize); - - m_string = buf; - delete[] buf; + else + { + // vsnprintf needs more space, so we have to allocate a new buffer and try again. + Vector<char> newBuffer(length + 1); + vsnprintf(newBuffer.data(), length + 1, formatString, argsCopy); + m_string = newBuffer; + } } -// ------------------------------------------------------------------------------------------------- -// -String StringList::join (const String& delim) +/*! + * \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. + */ +String StringList::join (const String& delimeter) { String result; for (const String &item : container()) { - if (result.is_empty() == false) - result += delim; + if (result.isEmpty() == false) + result += delimeter; result += item; } @@ -384,13 +424,17 @@ return result; } -// ------------------------------------------------------------------------------------------------- -// -bool String::mask_against (const String& pattern) const +/*! + * \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. + */ +bool String::maskAgainst (const String& pattern) const { // Elevate to uppercase for case-insensitive matching - String pattern_upper = pattern.to_uppercase(); - String this_upper = to_uppercase(); + String pattern_upper = pattern.toUpperCase(); + String this_upper = toUpperCase(); const char* maskstring = pattern_upper.chars(); const char* mptr = &maskstring[0]; @@ -432,71 +476,93 @@ return true; } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (short int a) +/*! + * \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 buf[32]; - ::sprintf (buf, "%d", a); - return String (buf); + char buffer[32]; + ::sprintf (buffer, "%d", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (int a) +/*! + * \brief Converts an integer into a string. + * \param value The value to convert. + * \returns the resulting string. + */ +String String::fromNumber (int value) { - char buf[32]; - ::sprintf (buf, "%d", a); - return String (buf); + char buffer[32]; + ::sprintf (buffer, "%d", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (long int a) +/*! + * \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 buf[32]; - ::sprintf (buf, "%ld", a); - return String (buf); + char buffer[32]; + ::sprintf (buffer, "%ld", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (unsigned short int a) +/*! + * \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 buf[32]; - ::sprintf (buf, "%u", a); - return String (buf); + char buffer[32]; + ::sprintf (buffer, "%u", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (unsigned int a) +/*! + * \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 buf[32]; - ::sprintf (buf, "%u", a); - return String (buf); + char buffer[32]; + ::sprintf (buffer, "%u", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (unsigned long int a) +/*! + * \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 buf[32]; - ::sprintf (buf, "%lu", a); - return String (buf); + char buffer[32]; + ::sprintf (buffer, "%lu", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// -String String::from_number (double a) +/*! + * \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 buf[64]; - ::sprintf (buf, "%f", a); - return String (buf); + char buffer[64]; + ::sprintf (buffer, "%f", value); + return String (buffer); } -// ------------------------------------------------------------------------------------------------- -// +/*! + * \returns the MD5-checksum of this string. + */ String String::md5() const { char checksum[33]; @@ -505,8 +571,11 @@ return String (checksum); } -// ------------------------------------------------------------------------------------------------- -// +/*! + * \brief Removes leading and trailing whitespace from this string. Alternatively a custom filter can be used to strip + * something else than whitespace. + * \param filter The filtering function to use. + */ void String::normalize (int (*filter)(int)) { int a = 0; @@ -524,8 +593,10 @@ 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;