// @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 );
}