Solution for
Programming Exercise 3.4
THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to
the following exercise from this on-line
Java textbook.
Exercise 3.4:
Write a program that reads one line of input text and breaks it up into
words. The words should be output one per line. A word is defined
to be a sequence of letters. Any characters in the input that are
not letters should be discarded. For example, if the user inputs
the line
He said, "That's not a good idea."
then the output of the program should be
He
said
that
s
not
a
good
idea
An improved version of the program would list "that's"
as a word. An apostrophe can be considered to be part of a word if
there is a letter on each side of the apostrophe.
To test whether a character is a letter, you might use
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z').
However, this only works in English and similar languages. A better choice
is to call the standard function
Character.isLetter(ch), which returns a boolean value of true
if ch is a letter and false if it is not. This works
for any Unicode character. For example, it counts an accented e,
é, as a letter.
Discussion
There are many ways to approach this problem, and probably all of
them are sort of tricky to get right. Here's a simple idea that
almost works: Go through all the characters in the string. If the
character is a letter, write it out. If it's not a letter, write
a carriage return instead. If line is a String
variable representing the line of text, this algorithm can be
coded as
for ( i = 0; i < line.length(); i++ ) {
ch = line.charAt(i);
if ( Character.isLetter(ch) )
TextIO.put(ch);
else
TextIO.putln();
}
This prints all the letters in a word on the same line of output.
Since words in the string are separated by non-letter characters,
and the computer prints a carriage return when it finds a non-letter,
words in the output are separated by carriage returns. But there
are is a problem with this: If two words are separated by
several non-letters in the string, then there will be
one or more blank lines between the words in the output. We don't
want to output two carriage returns in a row. To avoid this,
we can keep track of whether the previous output was a letter or
a carriage return. When we find a non-letter, we will only output
a carriage return if the previous output was not
a carriage return. To keep track of the necessary information,
I'll use a boolean variable named didCR.
The value of this variable will be true if the previous output
was a carriage return. I have to remember to set the value
of didCR each time I output something. With this modification,
the code becomes:
for ( i = 0; i < line.length(); i++ ) {
ch = line.charAt(i);
if ( Character.isLetter(ch) ) {
TextIO.put(ch);
didCR = false; // previous output was not a CR
}
else {
if ( didCR == false ) { // output CR if previous output was not a CR
TextIO.putln();
didCR = true; // previous output was a CR
}
}
}
The program requires an initial value for didCR. In the program below,
I output a carriage return before the for loop and set didCR to true.
You should be able to follow the rest of the program.
An entirely different approach to this problem is given by the algorithm,
"while there are more words in the string, get the next word and print it."
This turns out to be even harder to implement than the above.
The Solution
public class ListWordsInString {
/* This program will read one line of input typed by the user.
It will print the words from the input, one word to a line.
A word is defined to be a sequence of letters. All non-letters
in the input are discarded.
*/
public static void main(String[] args) {
String line; // A line of text, typed in by the user.
int i; // Position in line, from 0 to line.length() - 1.
char ch; // One of the characters in line.
boolean didCR; // Set to true if the previous output was a carriage return.
TextIO.putln("Enter a line of text.");
TextIO.put("? ");
line = TextIO.getln();
TextIO.putln();
didCR = true;
for ( i = 0; i < line.length(); i++ ) {
ch = line.charAt(i);
if ( Character.isLetter(ch) ) {
TextIO.put(ch);
didCR = false;
}
else {
if ( didCR == false ) {
TextIO.putln();
didCR = true;
}
}
}
TextIO.putln(); // Make sure there's at least one carriage return at the end.
} // end main()
} // end class
[ Exercises
| Chapter Index
| Main Index
]