Solution for
Programming Exercise 4.1


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 4.1: To "capitalize" a string means to change the first letter of each word in the string to upper case (if it is not already upper case). For example, a capitalized version of "Now is the time to act!" is "Now Is The Time To Act!". Write a subroutine named printCapitalized that will print a capitalized version of a string to standard output. The string to be printed should be a parameter to the subroutine. Test your subroutine with a main() routine that gets a line of input from the user and applies the subroutine to it.

Note that a letter is the first letter of a word if it is not immediately preceded in the string by another letter. Recall that there is a standard boolean-valued function Character.isLetter(char) that can be used to test whether its parameter is a letter. There is another standard char-valued function, Character.toUpperCase(char), that returns a capitalized version of the single character passed to it as a parameter. That is, if the parameter is a letter, it returns the upper-case version. If the parameter is not a letter, it just returns a copy of the parameter.


Discussion

We are told the name of the subroutine and that it has one parameter of type String. The name of the parameter is not specified. I will use str. The return type is void because the subroutine does not return a value. (It displays a value to the user, but to return a value means to return it to the line in the program where the function is called. The value returned by a function is generally not displayed to the user by the function.) The first line of the subroutine definition will be:

     static  void  printCapitalized( String str )

The subroutine must look at each character in str and decide whether to capitalize it or not. An algorithm for the subroutine is

     for each character in str:
         if the character is the first letter of a word:
             Print a capitalized version of the character
         else:
             Print the character
     Print a carriage return to end the line of output

The test as to whether a character is the first letter of a word is surprisingly complicated. A test that almost works is: "If the character is a letter and the preceding character is not a letter." The problem is that if the character is the first character is the string, then there is no preceding character. If the character is str.charAt(i), then the preceding character would be str.charAt(i-1), but str.charAt(i-1) doesn't exist if i is 0. Let's look at Java code that suffers from this bug. Recall that the operator "!" stands for "not."

     for ( i = 0;  i < str.length();  i++ ) {        // BUGGY CODE
         ch = str.charAt( i );
         if ( Character.isLetter(ch) && ! Character.isLetter(str.charAt(i-1)) )
             System.out.print( Character.toUpperCase(ch) );
         else
             System.out.print( ch );
     }
     System.out.println();

This will crash when i is zero. There are several ways to work around the problem, and all of them are techniques that are worth knowing. The first is to use a more complicated test in the if statement: "if the character is a letter and either it's the first character in the string or the previous character is not a letter". In Java, this is "if (Character.isLetter(ch) && (i==0 || !Character.isLetter(str.charAt(i-1))))". But it can be difficult to get such a complicated test right. Another possibility is a bit sneaky: Add an extra character onto the beginning of str, and then start the for loop with i=1. Any character will do, as long as it's not a letter. For example, you could say "str = "." + str;" Since the for loop starts at i=1, the "." is not copied to output, and the problem of i==0 doesn't arise. The method that I will use is similar, but it doesn't require any modification of str. I'll use another variable to represent the preceding character in the string, except that at the beginning of the string, I'll set it to the arbitrary value, '.'. At the end of the loop, the character that we have just processed becomes the "previous character" in the next iteration of the loop. Here is the complete subroutine, using this method:

       static void printCapitalized( String str ) {
          char ch;       // One of the characters in str.
          char prevCh;   // The character that comes before ch in the string.
          int i;         // A position in str, from 0 to str.length()-1.
          prevCh = '.';  // Prime the loop with any non-letter character.
          for ( i = 0;  i < str.length();  i++ ) {
             ch = str.charAt(i);
             if ( Character.isLetter(ch)  &&  ! Character.isLetter(prevCh) )
                 System.out.print( Character.toUpperCase(ch) );
             else
                 System.out.print( ch );
             prevCh = ch;  // prevCh for next iteration is ch.
          }
          System.out.println();
       }

This doesn't exhaust the possibilities. Another idea, for example, would be to use a boolean variable to keep track of whether the previous character was a letter.

Writing a main() routine to test this subroutine on a line of input is easy.


The Solution

    public class CapitolizeOneString {
      
       /*  This program will get a line of input from the user
           and will print a copy of the line in which the first
           character of each word has been changed to upper case.
           The program was written to test the printCapitalized
           subroutine.  It depends on the non-standard TextIO class.
       */
    
       public static void main(String[] args) {
          String line;  // Line of text entered by user.
          TextIO.putln("Enter a line of text.");
          line = TextIO.getln();
          TextIO.putln();
          TextIO.putln("Capitalized version:");
          printCapitalized( line );
       }  // end main()
       
       static void printCapitalized( String str ) {
             // Print a copy of str to standard output, with the
             // first letter of each word in upper case.
          char ch;       // One of the characters in str.
          char prevCh;   // The character that comes before ch in the string.
          int i;         // A position in str, from 0 to str.length()-1.
          prevCh = '.';  // Prime the loop with any non-letter character.
          for ( i = 0;  i < str.length();  i++ ) {
             ch = str.charAt(i);
             if ( Character.isLetter(ch)  &&  ! Character.isLetter(prevCh) )
                System.out.print( Character.toUpperCase(ch) );
             else
                System.out.print( ch );
             prevCh = ch;  // prevCh for next iteration is ch.
          }
          System.out.println();
       }

    }  // end class

[ Exercises | Chapter Index | Main Index ]