K&R C Exercise 1-12: Print Input One Word Per Line

Exercise 1-12. Write a program that prints its input one word per line.

Approach

A word is any contiguous run of non-whitespace characters; spaces, tabs, and newlines are all separators. The challenge is not reading words — it is knowing when to emit the newline that separates them in the output.

The solution borrows the two-state machine K&R introduce for their word-count program on page 20: a variable called state tracks whether the program is currently inside a word (IN) or outside one (OUT). The newline is printed exactly once — when the state transitions from IN to OUT on the first whitespace character after a word ends. Every subsequent whitespace character is ignored because the state is already OUT. This single rule handles any combination of spaces, tabs, and newlines without producing blank lines.

Solution

/* K&R Exercise 1-12 — print input one word per line              */
/* Compile: gcc -ansi -Wall ex1-12.c -o ex1-12                     */

#include <stdio.h>

#define IN  1   /* currently inside a word  */
#define OUT 0   /* currently outside a word */

int main(void)
{
    int c, state;

    state = OUT;
    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n' || c == '\t') {
            if (state == IN)   /* first whitespace after a word: end it */
                putchar('\n');
            state = OUT;
        } else {
            putchar(c);        /* non-whitespace: echo and stay/enter IN */
            state = IN;
        }
    }
    return 0;
}

How the State Machine Works

Step through the input "hello   world" character by character:

Character state before Action state after
h OUT print h IN
e IN print e IN
l IN print l IN
l IN print l IN
o IN print o IN
' ' (1st space) IN print \n — word ended OUT
' ' (2nd space) OUT nothing OUT
' ' (3rd space) OUT nothing OUT
w OUT print w IN
o, r, l, d IN print each char IN
EOF IN loop exits, no extra newline

Three spaces produce exactly one newline in the output — not three. And because the input ends mid-word (no trailing whitespace), the loop exits without printing a spurious final newline after world.

A Common Mistake to Avoid

A naive version emits the newline when entering a word (the OUT→IN transition):

/* Wrong — produces a blank first line and misplaced newlines */
if (c != ' ' && c != '\n' && c != '\t') {
    if (state == OUT)
        putchar('\n');   /* newline before the word, not after */
    putchar(c);
    state = IN;
}

This inserts a newline before each word instead of after the previous one. On the very first word the state is OUT, so it prints a blank line before any output at all. Emitting on IN→OUT avoids both problems.

Compile and Run

gcc -ansi -Wall ex1-12.c -o ex1-12

The program reads from standard input. Pipe text directly to see the results immediately:

echo "the quick   brown fox" | ./ex1-12

Or run interactively — type words and press Ctrl+D (Linux/macOS) or Ctrl+Z then Enter (Windows) to send EOF.

Sample Output

$ echo "the quick   brown fox" | ./ex1-12
the
quick
brown
fox

$ printf "one\t\ttwo\nthree" | ./ex1-12
one
two
three

$ printf "  leading and trailing  " | ./ex1-12
leading
and
trailing

The third example shows both edge cases at once: leading whitespace before the first word is silently skipped (state starts at OUT), and trailing whitespace after the last word produces no extra blank line (the IN→OUT transition fires but no new word follows).

What This Exercise Teaches

  • Two-state finite automaton. Using a single integer variable to remember context — whether the program is inside or outside a token — across iterations of a loop. This pattern reappears throughout K&R and in real lexers and parsers.
  • Emit output at the right transition. The newline is printed once when the state changes from IN to OUT, not once per whitespace character. Getting that timing right eliminates both missing separators and duplicate blank lines with no extra logic.
  • Symbolic constants improve readability. #define IN 1 and #define OUT 0 make the conditions self-documenting. if (state == IN) reads immediately; if (state == 1) does not.
  • Character-level I/O with getchar() and putchar(). Processing one character at a time keeps memory usage constant regardless of input length — no buffer or array needed.

Set Up Your C Environment

To compile and run this solution, you need GCC installed. If you have not set up C on your machine yet:

← Exercise 1-11  | 
Chapter 1 Solutions  | 
Exercise 1-13 →

Book:
The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>