> ### 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 ]