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
]