The String Class

The string class is extremely useful, in that it makes it easy for programmers to write efficient and safe code. In contrast, traditional C style ``character array'' strings are a never ending source of headaches and bugs. We explore the string class, as well as dicussing its advantages over character array strings.

Why Strings Are Good

One of the nice things about strings is that they store the string in automatically managed dynamic storage. This means that the programmer doesn't need to manage a strings memory. The programmer can use it just like they would use an integer or a double. The string grows when it needs to. This is an enormous advantage when reading data from an external source. For example, consider the following code;:

	char x[10];
	std::cin >> x; 

The C string, x only has enough room for 9 characters, so if the user types ``supercalifragilisticexpialidocious'', then the program could crash. On the other hand, the following code is safe:

	std::string x;
	std::cin >> x;
	
Because the string x can grow to a sufficient size to accomodate the use input. The size of x does not have to be decided at compile time.

Operations on Strings

For strings to be useful, they must support appropriate operations. The string class has the following methods:

Constructors

string();
string ( const string& );	// copy constructor
string ( const char* );	// c-style string

// creates a string of length length
// and fills it with the character c
string ( int length, char c);	

Testing String Size

Element Access

String Catenation

string& operator+ ( string& )

One may catenate strings using the operator +. For example, this gives rise to a new implementation of the popular program known as Hello World


#include <string>
#include <iostream>
int main()
{
    std::string s = "Hello ";
    std::string t = "World";
    std::cout << (s+t) << std::endl; 
    return 0;
}

One may also use the operator +=. Both operators are overloaded so that one may append type char.

Comparison Operators

ONe may compare two strings for equality using the usual comparison operator ==. The operators <, <= and >, >= are also defined -- they use lexical ordering for the comparisons.

Search Operations

string::size_type find_first_of(const string&),
string::size_type find_last_of(const string &),
string::size_type find_first_not_of(const string&),
string::size_type find_last_not_of(const string &)

These functions find (respectively) the first/last character that does/doesn't match any in the string (viewing the string argument as a list of charcters). There are similar functions that take a character argument and a character array string argument also. It returns the index. If nothing's found, string::npos is returned. For example,

#include <iostream>
#include <string>
int main()
{
    using std::string;
    using std::cout;
    using std::endl;

    string s = "Hello";
    string::size_type idx;

    idx	= s.find_first_of ("el");	// 1
    cout << idx << endl;

    idx	= s.find_first_not_of ("el");	// 0
    cout << idx << endl;
    
    idx = s.find_last_of ( "el" );	// 3
    cout << idx << endl;
    
    idx = s.find_last_not_of ("el"); // 4	
    cout << idx << endl;
    
    idx = s.find_first_of ("X"); // string::npos
    cout << idx << endl;
    
    idx = s.find_first_of (s); 	// 0
    cout << idx << endl;

    return 0;	
}

Do not confuse string::size_type with int. In particular, string::size_type is usually unsigned, int is signed.

Conversion to C Style Strings

const char* c_str() Returns a C style character array string. The returned string may not be modified by the caller. This function is very useful when we need to use functions that require an argumnt of type char*. For example,
	string s;
	ifstream(s);
	
Does not work, but
	string s;
	ifstream(s.c_str());
	
does.

Retrieving Substrings

string substr(size_type idx1, size_type idx2 )

Returns the substring beginning at idx1 and finishing at idx2. If the argument idx2 is not provided, it returns the substring starting at idx1 and ending at the end of the string.

Examples

Retrieving the file extension


#include <string>
#include <iostream>

using namespace std;

int main()
{
    string s;
    cout << "Enter a string" << endl;
    cin >> s;
    string::size_type idx = s.find_last_of ( '.' );
    if ( idx == string::npos )
    	cout << "No file extension";
    else
    	cout << "The file extension is " << s.substr ( idx ) << endl;

    return 0;
}

Finding the lexically minimal word in a list


#include <vector>
#include <string>
using namespace std;

int main()
{
    string s;
    vector<string> v;	
    
    cout << "Enter a word, 'q' to quit:" << endl;
    while ( getline(cin, s) && s != "q" ) 
    {
        v.push_back(s);	
        cout << "Enter a word, 'q' to quit:" << endl;
    }	
    
    
    int sz = v.size();
    string min=v[0];
    for ( int i = 0; i < sz; ++i )
    if ( v[i] < min ) 
    {
        min = v[i];
    }	
    cout << "The minimum word is '" << min << "'" << endl;	
}

A Simple Hangman-like Game


#include <cstdlib>
#include <fstream>
#include <ctime>
#include <stdexcept>


// these two constants are system dependent.
const char DICTIONARY[] = "/usr/dict/words";
const int NUM_WORDS = 45402;
using namespace std;

// see the tutorial on random numbers
int int_rand(int n)
{
	if ( n <= 0 || n > RAND_MAX )
		throw domain_error("Argument out of range");
	const int bucket_size = RAND_MAX / n;
	int a;
	do 
	{
		a = rand() / bucket_size;
	}
	while ( a >= n );

	return a;
}

int main()
{
    srand(time(0));
    string word;
    ifstream fin(DICTIONARY);

    int r = int_rand ( NUM_WORDS );

    if (!fin)
        exit(EXIT_FAILURE);
    

    for ( int i = 0; i < r; ++i )
        getline(fin, word);

    // now we're ready to play.

    string current ( word.size(), '_' );

    int guesses = 0;
    char ch;
    while ( current != word )
    {
        cout << "The word is :\n"
            << current << endl;
        cout << "Choose a letter" << endl;
        cin >> ch;
        for ( int i = 0; i<word.size(); ++i )
            if ( word[i] == ch )
                current[i] = ch;
        ++guesses;    
        cout << "You've had " << guesses << " guesses" << endl;
    }
    return 0;
}