4.12. Converting a String to Lower- or Uppercase

Problem

You have a string that you want to convert to lower- or uppercase.

Solution

Use the toupper and tolower functions in the <cctype> header to convert characters to upper- or lowercase. Example 4-20 shows how to do it using these functions. See the discussion for an alternative.

Example 4-20. Converting a string’s case

#include <iostream>
#include <string>
#include <cctype>
#include <cwctype>
#include <stdexcept>

using namespace std;

void toUpper(basic_string<char>& s) {
   for (basic_string<char>::iterator p = s.begin();
        p != s.end(); ++p) {
      *p = toupper(*p); // toupper is for char
   }
}

void toUpper(basic_string<wchar_t>& s) {
   for (basic_string<wchar_t>::iterator p = s.begin();
        p != s.end(); ++p) {
      *p = towupper(*p); // towupper is for wchar_t
   }
}

void toLower(basic_string<char>& s) {
   for (basic_string<char>::iterator p = s.begin();
        p != s.end(); ++p) {
      *p = tolower(*p);
   }
}

void toLower(basic_string<wchar_t>& s) {
   for (basic_string<wchar_t>::iterator p = s.begin();
        p != s.end(); ++p) {
      *p = towlower(*p);
   }
}

int main() {

   string s = "shazam";
   wstring ws = L"wham";

   toUpper(s);
   toUpper(ws);

   cout << "s =  " << s << endl;
   wcout << "ws = " << ws << endl;

   toLower(s);
   toLower(ws);

   cout << "s =  " << s << endl;
   wcout << "ws = " << ws << endl;
}

This produces the following output:

s =  SHAZAM
ws = WHAM
s =  shazam
ws = wham

Discussion

One would think that the standard string class has a member function that converts the whole thing to upper- or lowercase, but, in fact, it doesn’t. If you want to convert a string of characters to upper- or lowercase, you have to do it yourself, sort of.

Not surprisingly, there is more than one way to convert a string’s case (and when I say “string,” I mean a sequence of characters, either narrow or wide). The simplest way to do it is with using one of the four-character conversion functions toupper, towupper, tolower, and towlower. The first form of each of these is the narrow character version; the second form (with the extra “w”) is its wide character equivalent.

Each of these functions converts the case of the character using the current global locale’s rules for case conversion. Upper- and lowercase depend on the characters being used in the current locale; some characters don’t have an upper- or lowercase version, in which case the functions listed above will return the same character you pass in. See Chapter 13 for more information on locales. The C++ facilities for dealing with different locales are complicated, and I cannot do them justice here.

Doing the actual character conversion is easy. Consider the toUpper function in Example 4-20:

void toUpper(basic_string<char>& s) {
   for (basic_string<char>::iterator p = s.begin();
        p != s.end(); ++p) {
      *p = toupper(*p);
   }
}

The line in bold does the real work. The version for wide characters is nearly identical:

void toUpper(basic_string<wchar_t>& s) {
   for (basic_string<wchar_t>::iterator p = s.begin();
        p != s.end(); ++p) {
      *p = towupper(*p);
   }
}

I overloaded toUpper for the different character types because there is no fully generic toupper function to convert a character’s case (unless you are using facets from the <locale> header, which I discuss below). Two simple functions, as above, will get the job done.

There is another way to do this though, and the motivating factor for using this second approach would be your need to use explicit locales. The following versions of toUpper and toLower convert the case of a string, regardless of its character type, as long as the named locale (which defaults to the current locale) supports case conversion for that locale and character type.

template<typename C>
void toUpper2(basic_string<C>& s, const locale& loc = locale()) {
   typename basic_string<C>::iterator p;
   for (p = s.begin(); p != s.end(); ++p) {
      *p = use_facet<ctype<C> >(loc).toupper(*p);
   }
}

template<typename C>
void toLower2(basic_string<C>& s, const locale& loc = locale()) {
   typename basic_string<C>::iterator p;
   for (p = s.begin(); p != s.end(); ++p) {
      *p = use_facet<ctype<C> >(loc).tolower(*p);
   }
}

The lines in bold do the real work. Functionally, they work the same as the upper- and lowercase functions used in Example 4-20, except that they use the internationalization facilities in the <locale> header to do it. See Chapter 13 for a more thorough discussion of locales, facets, and internationalization.

Get C++ Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.