String Examples

Read time: 17 minutes (4494 words)

We need to work through a problem involving strings that is a bit more involved than the simple example we worked on last time. How about writing a “palindrome” checker.

What is a palindrome?

A palindrome is a word or phrase that is the same forwards and backwards. A simple example of one is “anna”. A classic example is “Able was I ere I saw Elba”.

Now, we cannot simply reverse the sequence of characters and see the palindrome. What we need to do is check that the letters of the test string are identical forwards and backwards. So, how do we do this?

Support functions

To check if a given string is a palindrome, we need to change all the letters in the string to lower case, then get rid of things like punctuation or spaces. After doing all of that, we need to reverse the string and compare it to the original to see if they are equal!

Here is some code that could confirm if a sample string is a palindrome:

bool check_palindrome(string str) {
    string s1, s2, s3;
    s1 = strip_extra(lower_case(str));
    s2 = strip_extra(lower_case(reverse(str)));
    return s1 == s2;
}

Here, we have introduced a few supporting functions that take an input string and return a modified output string:

  • strip_extra - removes non letter characters

  • lower_case - returns the input string converted to lower case

  • reverse - returns the input string reversed.

This is simple. Here is a program that we might use to test our ideas:

int main(int argc, char **argv) {
    string lcstr;
    string reversed;
    string strippedstr;

    for(int i=0;i<5;i++) {
        cout << "\"" << testset[i] << "\"";
        if(check_palindrome(testset[i]))
            cout << " is a ";
        else
            cout << " is not a ";
        cout << "palindrome" << endl;
    }
}

In this chunk of code, we assume we have setup an array of test strings, and we want to output the string and a statement indicating if that string is, or is not, a palindrome.

Example strings

I did what everyone is expected to do today, Googled for a few Palindromes! This is what I got:

string testset[5] = {
    "A Toyota's a Toyota",
    "Able was I ere I saw Elba",
    "A Santa at NASA",
    "Are we not drawn onward to new era",
    "This is not a palindrome"
};

The supporting routines

All we need to do to complete this project is to build our supporting routines.

lower_case()

Let’s start off with an easy one, converting to lower case.

To build this routine, we need to check each character, easy enough to do in a simple loop. However, we might need to change any letter, and we run into a potential issue. Can we alter the string “in place”?

C++ is an evolving standard. In older versions of the language, it might not be possible to do a simple change. As an example, what happens if you try something like this:

string test = "demo string";
test[0] = 'D';
test[5] = 'S';
test[10] = '!';

Should this be possible?

In our old way of thinking, a string is just an array of characters. Adding another character at the end of the current string would surely botch things, since we know (or should know) that the string probably has some indicator that we have asked to modify something beyond the current length of the string.

But a string in C++ is a proper class, and the authors of that class thought through all the ways folks might like to (sb)use their creation.

So how do we figure out what we can really do with a string.

Well Luke, use the Force!

Actually, use the documentation!

So, to build this routine safely, I needed to search the documentation for the string class and see what functions (or methods as they are properly called) are available. I found a simple method named append that lets you add something to an existing string, and that seemed to be what I needed.

Note

Actually, this append method is a good example of “polymorphism”, somehting we will look at next class. For now, it just means we can “append” lots of different things to a string, including a single character, which is what I will use in this example code!

I decided that the easy way to build the final string I want would be to create an empty string, then add (append) a single character at a time, converting that character to lower case as required. There is a nice C++ function I can use to do this: tolower(), which takes a single character and returns the lower case version of that character. It is smart enough not to try to lower case numbers or punctuation, so this code will do what we need!

Note

BTW! Python does not let you modify a string in place, you mist change the string by creating a new string from the old one, so this logic is exacty what you would do in Python.

Here is the code we need:

string lower_case(string str) {
    int len = str.size();
    string out = "";
    for(int i=0;i<len;i++)
        out.append(1,tolower(str[i]));
    return out;
}

The first parameter to the append function is the size (in bytes) of the thing you are adding. One byte in this code.

strip_extra()

Using this same pattern, we can strip the string of unwanted characters. All we need to do here is check each character to make sure it is a letter. I am going to assume that we only call this function after we lower case all the letters in the string This is the code I ended up with:

string strip_extra(string str) {
    int len = str.size();
    char c;
    string out = "";
    for(int i=0;i<len;i++) {
        c = str[i];
        if(c >='a' && c <='z') 
            out.append(1,str[i]);
    }
    return out;
}

reverse()

The last function we need takes an input string and returns that string in reverse. Once again, we use the same pattern to build up the output string. We use a for-loop that indexes the string in reverse to get this done. Here is the code:

string reverse(string str) {
    int len = str.size();
    char c;
    string out = "";
    for(int i=0;i<len;i++) {
        c = str[len-i-1];
        out.append(1,c);
    }
    return out;
}

Wrappin up

Here is the complete program:

// Palindrome.cpp - checks a series of palindromes

# include <iostream>
using namespace std;

string testset[5] = {
    "A Toyota's a Toyota",
    "Able was I ere I saw Elba",
    "A Santa at NASA",
    "Are we not drawn onward to new era",
    "This is not a palindrome"
};

string lower_case(string);
string strip_extra(string);
string reverse(string);
bool check_palindrome(string);

int main(int argc, char **argv) {
    string lcstr;
    string reversed;
    string strippedstr;

    for(int i=0;i<5;i++) {
        cout << "\"" << testset[i] << "\"";
        if(check_palindrome(testset[i]))
            cout << " is a ";
        else
            cout << " is not a ";
        cout << "palindrome" << endl;
    }
}

bool check_palindrome(string str) {
    string s1, s2, s3;
    s1 = strip_extra(lower_case(str));
    s2 = strip_extra(lower_case(reverse(str)));
    return s1 == s2;
}

string lower_case(string str) {
    int len = str.size();
    string out = "";
    for(int i=0;i<len;i++)
        out.append(1,tolower(str[i]));
    return out;
}

string strip_extra(string str) {
    int len = str.size();
    char c;
    string out = "";
    for(int i=0;i<len;i++) {
        c = str[i];
        if(c >='a' && c <='z') 
            out.append(1,str[i]);
    }
    return out;
}

string reverse(string str) {
    int len = str.size();
    char c;
    string out = "";
    for(int i=0;i<len;i++) {
        c = str[len-i-1];
        out.append(1,c);
    }
    return out;
}

Here is the output produced by this code:

"A Toyota's a Toyota" is a palindrome
"Able was I ere I saw Elba" is a palindrome
"A Santa at NASA" is a palindrome
"Are we not drawn onward to new era" is a palindrome
"This is not a palindrome" is not a palindrome