Noeud:Basic String Usage, Noeud « Next »:, Noeud « Up »:Strings



Basic String Usage

Constructing a string object is easy, and the following code shows a number of different ways to create strings, and perform some simple operations on them:

     /* string1.cc
        Compiled using g++ string1.cc -o string1 */
     #include <string>
     
     int main()
     {
       char *cstring = "third=c_string\0";
       string first("first");
       string second("2nd string", 3);
       string third(cstring);
       string fourth(4, '4');
       string five("This is the fifth");
       string fifth(five, five.find("fifth"));
     
       cout << first << ", " << second << ", "
            << third << ", " << fourth << ", " << fifth << endl;
     
       string line(first+", "+second+", "+third.substr(0, 5)+
     	      ", "+fourth.substr(3)+"th, "+fifth);
       cout << line << endl;
     }
     
     Example 3.26: string1.cc: examples of creating strings
     

The output is as follows:

     $ ./string1
     first, 2nd, third=c_string, 4444, fifth
     first, 2nd, third, 4th, fifth
     

We begin by creating a C string, then follow up by creating five string objects. The first string we create contains the character array we pass in - "first". The constructor for second takes a character array and initialises the string to have the first 3 characters from the array, so it contains the string "2nd". The constructor for third takes the C string "third=c_string\0". The string variable fourth is created by passing a character and a number; the resultant string is made up the character repeated n times (so in this case, fouth is made up of "4444". Finally, we create a string called five from the character array "This is the fifth", and use that string to initialise the string fifth. Notice that we are making a call to find within the constructor to fifth; find returns the first position, if it exists, of the occurrence of the string passed in to it. Since the string "fifth" indeed exists, the result - 12 - is passed back, and fifth is created by taking the twelfth character (and all beyond) of string five.

We then print all of these initialised strings (see the output above), and create a new string called line that will contain copies of the original strings, slightly modified. We call substr twice within the creation of line; substr, when passed 2 integers, returns the string represented by the start of the first position, counting as many characters as are passed in for the second argument. So calling substr(0, 5) on "third=c_string\0" will return "third". Just passing one integer to the call to substr means that we take all characters starting from the position passed in. So substr(3) on "4444" will return the element at index 3 and beyond, which is a '4'. The result is that line, when printed comes out as

     first, 2nd, third, 4th, fifth
     

As you can see, we've performed some relatively complex string manipulations with just a few lines of code.

Let's look more closely now at finding items within a string. The previous example was contrived because we planned all along for things to go our way; by this, I mean that we knew that the find calls would return the values that we were interested in. But what value is returned when we fail to find a position within a string? The answer lies in looking at the value npos. npos is defined within the string namespace, and defines the maximum size a string can be. When a search function fails to find part of a string, it returns npos, which we need to check against in order to ascertain whether the find worked or not. At the surface level, it's very useful, although as we'll see in a minute, there are a few pitfalls to be wary of. First though, an example:

     /* string2.cc
        Compiled using g++ string2.cc -o string2 */
     #include <string>
     
     int main()
     {
       std::string::size_type i;
       string sentence("Mary had a little lamb, his\
      fleece was as white as snow...");
       i = sentence.find("You'll never find this...");
     
       if (i == std::string::npos)
         cout << "i == npos; failed to find string.\n";
     
       cout << sentence.substr(0, sentence.rfind(" lamb")) << " "
            << sentence.substr(sentence.find("fleece"), 6) << endl;
     
       cout << sentence.substr(0, sentence.rfind("Again, no such string"))
            << endl;
     
       i = 0;
       int num = 0;
       /* Find out how many 'a's there are in the string 'sentence': */
       while(i != std::string::npos)
         {
           i = sentence.find("a", i);
           if (i != std::string::npos)
     	{
     	  num++;
     	  i++;
     	}
         }
       cout << "Found " << num << " occurrences of 'a'" << endl;
       exit(0);
     }
     
     Example 3.27: string2.cc: finding things within a string
     

The output goes like this:

     $ ./string2
     i == npos; failed to find string.
     Mary had a little fleece
     Mary had a little lamb, his fleece was as white as snow...
     Found 7 occurrences of 'a'
     $
     

Let's discuss the code. To begin with we create a variable i of type size_type and assign it to npos. After declaring and initialising the string sentence, we run the find function on sentence, passing in the string "You'll never find this...". The result is assigned to i. If the search would have succeeded, it would've returned the index of the first element of the string we're searching for; but since the string we're searching for does not exist within sentence, npos is returned, and the evaluation i == std::string::npos will be true, since find returns npos because it failed to find the search string.

The statement

       cout << sentence.substr(0, sentence.rfind(" lamb")) << " "
            << sentence.substr(sentence.find("fleece"), 6) << endl;
     

includes a new function call to rfind, as well as doing some more substring manipulation. rfind is similar to find, except that it searches back through the string in question instead of forwards. It returns the first position of the string it is searching for as it occurs from the end of the string. Since the string " lamb" exists, find returns the relevant position and "Mary had a little" is retrieved as the substring. The second part of the cout statement uses the find method as we'd expect it to work, and "fleece" is extracted (recall that substring returns the string starting from the first argument and counting as many characters as there are in the second argument). The result is that the string "Mary had a little fleece" is printed.

However, the following statement

       cout << sentence.substr(0, sentence.rfind("Again, no such string"))
            << endl;
     

is different and deceiving; we use rfind to look for a string that clearly isn't in the string sentence. Since we're trying to create a substring from the start of sentence, to sentence.rfind("Again, no such string"), what will be printed out? The answer is that the entire sentence string will be printed, because rfind failed and as a result returned npos. And because npos returns the maximum (unsigned) value of its type, and the length of sentence is clearly less than that value, the cout statement just prints out the string in its entirety.

What we should have done is something like this:

       long pos = sentence.rfind("Again, no such string");
       /* If we've found what we're looking for, print the string out,
          or do whatever else we want: */
       if (pos != npos)
         cout << sentence.substr(0, pos)
              << endl;
       else /* The find failed, so do something else ... */
     

So, be warned! Always check the return value of a find() or rfind() method, to see if it is equal to npos or not. If it is equal to npos and you don't check for it, the example code above in string2.cc illustrates what could happen.

These are just a few of the operations we can perform with a string, and there are plenty of others that are all just as intuitive to use, such as insert(), erase() and replace(), amongst many others; since they are easy to understand they're in the String Summary, if you want to see the full range of operations you can use.

In the last section of string2.cc, we counted the number of occurrences of the character 'a' that occur within sentence. It's fairly routine what we're trying to achieve here, so no code-breakdown is necessary. However, this section of code is undeserving; we can greatly reduce the amount of code for such a simple operation by using string iterators and a generic algorithm...