// @topic W040506 substring class with overloaded operators // @brief class substring implementation // CIS-255 substring class #include "substring.h" // Constructor that takes a reference to a string: substring::substring( std::string& str ) : m_str( str ), m_begin( 0 ), // this ctr puts starting pos at the beginning of the string m_end( str.length() ) // end of the string { /* The substring class is entirely dependent on the std::string object which has to be passed to each constructor. It will be refferred to as "underlying string object" in our comments. DONE: Add initializer list to this constructor, or your code won't compile. DONE: Add two member variables to accommodate the substring positions, size_t m_begin; // absolute offset of the substring beginning size_t m_end; // absolute offset of the substring ending and set these positions to match the entire string. */ } // Constructor that takes string reference, beginning, and ending positions substring::substring( std::string& str, size_t pos_begin, size_t pos_end ) : m_str( str ), m_begin( pos_begin ), m_end( pos_end ) { /* In addition to std::string object, this constructor takes two absolute positions to initialize m_begin and m_end offsets of the substring object. DONE: Add initializer list to this constructor, or your code won't compile. */ } // Return copy of the substring as std::string object std::string substring::str() const { /* This function returns a copy of the string object, initialized by the characters of the substring. DONE: modify the following statement to construct and return the appropriate string: */ //std::string temp = m_str.substr( m_begin, size() ); return m_str.substr( m_begin, size() ); } // Return substring size size_t substring::size() const { /* DONE: Calculate and return the length of the substring, which is the distance between the beginning and the ending positions. */ return m_end - m_begin; } // Return beginning position size_t substring::begin() const { /* DONE: Return the beginning position. */ return m_begin; } // Return ending position size_t substring::end() const { /* DONE: Return the ending position. */ return m_end; } // Set beginning position void substring::begin( size_t pos ) { /* DONE: Add code to set the beginning position. */ m_begin = pos; } // Set ending position void substring::end( size_t pos ) { /* DONE: Add code to set the ending position. */ m_end = pos; } // The substring content is set to an empty string void substring::clear() { /* Hint: call replace() member function of the underlying string object to replace the current substring with an empty string. DONE: because clear changes in the underlying substring, the function must update the beginning and ending positions accordingly: BEFORE clear: sub.clear(); m_str: XXXX?????????????????YYYYYYYYY | | '-m_begin `-m_end AFTER clear: m_str: XXXXYYYYYYYYY | +--m_end: got adjusted | '-m_begin */ m_str.replace( m_begin, size(), "" ); m_end = m_begin; } // Replace substring with str substring& substring::replace( std::string const& str ) { /* Note that this function follows the logic of std::string::replace(). DONE: because replace causes changes in the underlying substring, the function must update the beginning and ending positions according to the length of the new content. For example, BEFORE replace: sub.replace( "abc" ); m_str: XXXX?????????????????XXXXXXXXXX | | '-m_begin `-m_end AFTER replace: m_str: XXXXabcXXXXXXXXXX | | | `-m_end: got adjusted '-m_begin */ m_str.replace( m_begin, size(), str ); m_end = m_begin + str.length(); return *this; } // Fill substring with a particular character substring& substring::fill( char ch ) { /* DONE: write a loop that will replace every character that belongs to the substring with the character passed here as the input parameter. Hint: use subscript access to the underlying string object: m_str[ position ] = ch; where position is every offset that belongs to the open range m_begin <= position < m_end */ size_t position = m_begin; for ( ; position < m_end; ++position ) { m_str[ position ] = ch; } return *this; } // Insert str at the specified position: substring& substring::insert( std::string const& str, size_t pos ) { /* Hint: use insert() member function of std::string. TODO: because insert causes changes in the underlying substring, the function must compare its beginning and ending against the insertion position and the length of the string being inserted. The substring needs to adjust its beginning and ending positions as demonstrated by the following diagrams: Example 1. BEFORE insert: insert( "abc", pos ) | .--' | m_str: XXXX?????????????????XXXXXXXXXX | | '-m_begin `-m_end AFTER insert: | m_str: XXabcXX?????????????????XXXXXXXXXX | | '-m_begin: `-m_end: got adjusted got adjusted Example 2. BEFORE insert: insert( "abc", pos ) | `-------. | m_str: XXXX?????????????????XXXXXXXXXX | | '-m_begin `-m_end AFTER insert: | m_str: XXXX?????????abc???????XXXXXXXXXX | | '-m_begin `-m_end: got adjusted IMPORTANT: Make your own decision regarding two special cases when the insertion occurs exactly at the beginning, or exactly at the ending of the substring. Make sure to document your coding in the comments of your program. */ return *this; } // Convert substring to UPPER CASE substring& substring::toupper() { /* TODO: write a loop that will replace every character that belongs to the substring with the corresponding UPPER CASE character. Hint: use toupper() library function and and subscript access to the underlying string object: toupper( m_str[ position ] ); where position is every offset that belongs to the open range m_begin <= position < m_end */ return *this; } // Convert substring to lower case substring& substring::tolower() { /* Similar to the above. */ return *this; } // Search for a new substring starting at the specified position: size_t substring::search( const std::string& str, size_t pos ) { /* TODO: use find() of the underlying string and update the beginning and the ending positions of the substring. TODO: when find() call fails, it returns back the member value std::string::npos. In such case the substring must not update its positions and also return std::string::npos back to the caller: */ return std::string::npos; } // The string content is set to the size of entire string void substring::expand() { /* TODO: Update substring positions to match the entire underlying string. */ } // Equality test between two substrings bool substring::equals_to( substring const& other ) const { /* TODO: Compare this substring with another character by character. The test should succeed if every character in both substrings matches exactly. Return true, if the underlying text fragments are the same. Return false otherwise. */ return false; } // Equality test between substring and std::string bool substring::equals_to( std::string const& str ) const { /* TODO: Compare this substring with a string character by character. The test should succeed if every character matches exactly in both. Return true, if the underlying text fragments are the same. Return false otherwise. */ return false; } // Merge with another substring substring& substring::merge( substring const& other ) { /* TODO: Modify the substring positions so that new beginning = min( this->beginning, other.beginning ) new ending = max( this->ending, other.ending ) */ return *this; } // Swap values with another substring void substring::swap( substring& other ) { /* TODO: Replace content of this substring with the content of the other, and vice versa. TODO: Make sure that the substrings don't overlap. Save the content of both substrings in two std::string objects and then invoke replace. */ } // Prepare substring for parsing substring& substring::parse() { /* This function initializes the substring preparing the object for character parsing. TODO: add two new member variables to the definition of the substring class: // Character parsing state variables: size_t m_parse_begin; // position where parsing starts bool m_parse_success; // flag to indicate the parsing succeeded and set them here as follows: m_parse_begin = m_begin; m_parse_success = true; Thus, parse() remembers the position at which the character matching starts: m_str: XXXX?????????????????XXXXXXXXXX | | +-m_begin `-m_end | `-m_parse_begin m_parse_success == true */ return *this; } // Test if substring is alphabetic substring& substring::isalpha() { /* TODO: if m_parse_success is equal to false, the function should do nothing and return immediately: if ( m_parse_success == false ) { // Nothing to do: the test has already failed prior // to this call: return *this; } // Parser state is ok, proceed: // ... TODO: use character by character scanning to test the characters of the underlying substring as follows: if ( isalpha( m_str[ position ] ) ) The code uses standard library character classification function isalpha(ch), which returns non-zero if character ch is alphabetic, zero if not. After successful test m_begin position should be rolled forward to point to the next unparsed character in the underlying substring as shown by the following diagram: parsed characters unparsed characters \____ ____________/ \ \/ / m_str: XXXXabcd????????????XXXXXXXXXX | | | | `-m_begin `-m_end | `-m_parse_begin m_parse_success == true IMPORTANT: the test should stop immediately as soon as m_end position is reached. The success of the test is measured by the number of good matches. If one or more matches are found, the function rolls m_begin forward and returns. If no good matches are found (or if the substring is empty), the function should set m_parse_success flag to false and return: m_parse_success = false; return *this; */ return *this; } // Test if substring is upper case substring& substring::isupper() { /* TODO: implement exactly the same logic as substring::isalpha(), but using standard library character classification function isupper(ch), which returns non-zero if character ch is UPPER CASE, zero if not. TODO: Remember to check the parser state at the very beginning: if ( m_parse_success == false ) { // Nothing to do: the test has already failed prior // to this call: return *this; } // Parser state is ok, proceed: // ... */ return *this; } // Test if substring is lower case substring& substring::islower() { /* TODO: implement exactly the same logic as substring::isalpha(), but using standard library character classification function islower(ch), which returns non-zero if character ch is lower case, zero if not. */ return *this; } // Test if substring is digit substring& substring::isdigit() { /* TODO: implement exactly the same logic as substring::isalpha(), but using standard library character classification function isdigit(ch), which returns non-zero if character ch is digit, zero if not. */ return *this; } // Test if substring is alphabetic or digit substring& substring::isalnum() { /* TODO: implement exactly the same logic as substring::isalpha(), but using standard library character classification function isalnum(ch), which returns non-zero if character ch is alphabetic or digit, zero if not. */ return *this; } // Test if substring is blank, tab, newline, return, formfeed, or vertical tab substring& substring::isspace() { /* TODO: implement exactly the same logic as substring::isalpha(), but using standard library character classification function isspace(ch), which returns non-zero if character ch is blank, tab, newline, return, formfeed, vertical tab, zero if not. */ return *this; } // Test if substring is matches text specified by str substring& substring::isstr( const std::string& str ) { /* TODO: implement exactly the same logic as substring::isalpha(), but compare the underlying substring character by character with the text provided in str parameter. */ return *this; } // Test if substring is empty substring& substring::isempty() { /* TODO: This test succeeds if the underlying substring is empty, fails otherwise: if ( m_parse_success == false ) { // Nothing to do: the test has already failed prior // to this call: return *this; } else if ( m_begin == m_end ) { // Success: the substring is empty: return *this; } else { // Test failed: the substring is not empty: m_parse_success = false; return *this; } */ return *this; } // Return true if parsing succeeded, false otherwise bool substring::succeeded() const { /* TODO: simply return the parser result to the caller: return m_parse_success; */ return false; } // Return matched substring substring substring::match() const { /* TODO: construct and return the substring object that matches the substring of successfully parsed characters: parsed characters unparsed characters \____ ____________/ \ \/ / m_str: XXXXabcd????????????XXXXXXXXXX | | | | `-m_begin `-m_end | `-m_parse_begin m_parse_success == true This is easy, all you have to write is: return substring( m_str, m_parse_begin, m_begin ); */ return *this; } // Undo changes caused by parsing substring& substring::unparse() { /* TODO: restore the original m_begin position of the substring and set the parsing flag to true: m_begin = m_parse_begin; m_parse_success = true; Thus, unparse() prepares the substring for another parsing attempt: m_str: XXXX?????????????????XXXXXXXXXX | | +-m_begin `-m_end | `-m_parse_begin m_parse_success == true */ return *this; } //---------------------------------------------- // Operators overloaded as members functions: //---------------------------------------------- // Assignments: substring& substring::operator=( substring const& other ) { if ( this == &other ) { // avoid self-assignment return *this; } replace( other.str() ); return *this; } substring& substring::operator=( std::string const& str ) { replace( str ); return *this; } // Merge with another substring: substring& substring::operator+=( substring const& other ) { merge( other ); return *this; } // Compare with another substring: // ( us > other ) bool substring::operator>( substring const& other ) const { // ( m_str > other.m_str ) + return ( 0 < m_str.compare( m_begin, size(), other.m_str, other.m_begin, other.size() ) ); } bool substring::operator<( substring const& other ) const { return ( 0 > m_str.compare( m_begin, size(), other.m_str, other.m_begin, other.size() ) ); } // Subscript access to individual characters within substring: char& substring::operator[]( size_t idx ) { return m_str[ m_begin + idx ]; } char substring::operator[]( size_t idx ) const { return m_str[ m_begin + idx ]; } // Operators overloaded as non-member functions: std::ostream& operator<<( std::ostream& os, substring const& sub ) { os << sub.str(); return os; } // Equality/Inequality tests between two substrings: bool operator==( substring const& one, substring const& another ) { //return one.str() == another.str(); return ( 0 == one.m_str.compare( one.m_begin, one.size(), another.m_str, another.m_begin, another.size() ) ); } bool operator!=( substring const& one, substring const& another ) { return !operator==( one, another ); //return !( one == another ); } // Equality/Inequality tests between substring and std::string: bool operator==( std::string const& str, substring const& sub ) { //return str == sub.str(); return ( 0 == str.compare( 0, str.length(), sub.m_str, sub.m_begin, sub.size() ) ); } bool operator==( substring const& sub, std::string const& str ) { return operator==( str, sub ); } bool operator!=( std::string const& str, substring const& sub ) { return !operator==( str, sub ); } bool operator!=( substring const& sub, std::string const& str ) { return !operator==( str, sub ); } // Equality/Inequality tests between substring and zero-terminated string: bool operator==( substring const& sub, char const* cstr ) { //return cstr == sub.str(); return ( 0 == sub.m_str.compare( sub.m_begin, sub.size(), cstr ) ); } bool operator!=( substring const& sub, char const* cstr ) { return !operator==( sub, cstr ); } bool operator==( char const* cstr, substring const& sub ) { return operator==( sub, cstr ); } bool operator!=( char const* cstr, substring const& sub ) { return operator==( sub, cstr ); }