C 教材:廣義字,if-else|| 算子

定義廣義字 (general word) 是 以一個或連續若干多個:空格、tab、LF 字元所隔開的任意一串可見字元 (printable)。例如

while((c = getchar()) != EOF)
裡面有 5 個廣義字:while((c=getchar())!=EOF)。 這也就是 wc 程式中對所謂「字」 (word) 的定義, 同時也是 vi 程式中用 WBE 指令所定義的「字」。

以下我們要寫一個程式,列出純文字輸入檔中有幾個廣義字。 這個程式的一個主要觀念上的技巧,就是要知道目前讀到的字元在什麼狀態。 我們用 state 代表狀態。 此處只有兩種狀態:在一個廣義字的裡面或外面。 為了增加程式的可讀性,我們用兩個符號常數 INOUT 來代表這兩種狀態。 state 原本是 OUT。 當我們從 OUT 狀態首次遇見可見字元時, state 改為 IN。 一直到首次遇見空格、tab 或 LF 字元,才改為 OUT。 每進入 IN 一次,就記錄一次廣義字的個數。


#include <stdio.h>
#define IN 1
#define OUT 0
 
/* 簡化的 UNIX wc -w 程式  (mywc.c) */
main() {
    int c, nw=0, state=OUT;
 
    while((c = getchar()) != EOF) {
        if (c == ' ' || c == '\t' || c == '\n')
            state = OUT;
        else if (state == OUT) {
            state = IN;
            ++nw;
        }
    }
    printf("%d\n", nw);
}

在第一個 if 結構的 CONDITION 中, 我們用了 || 算子, 它是 OR 的意思。它的優先度是 3,左傾。 而且我們看到 if-else 結構。 它的基本形式是
if (CONDITION) STATEMENTS_1 else STATEMENTS_0
CONDITION 成立,執行 STATEMENTS_1, 否則執行 STATEMENTS_0。 不論 STATEMENTS_1STATEMENTS_0, 執行結束之後,都直接跳去執行 STATEMENTS_0 的下一個指令。

在前面的程式中,若遇到新輸入的字元是空格、tab 或 LF, 就把 state 設定為 OUT。 若連續出現這些字元也沒關係,反之 state 總是 OUT。 否則就是輸入了可見字元。 但是只有第一次從 OUT 狀態遇到可見字元的時候才算一個字, 而且將 state 變成 IN。 否則不做任何事。

在上面的 if-else 結構中,STATEMENTS_0 只有一個指令,就是

if (state == OUT) { state = IN; ++nw; }
這個指令。雖然看起來有很多,但是在結構上,它就是一個 if 結構而已, 算是一個指令。 因為只有一個指令,所以不必放在 { } 裡面。

這個程式的輸出,應該和 wc -w 的結果一樣。

習題

  1. 寫一個程式,將輸入的純文字檔中每一列開頭的連續空白 (包括空格和跳格) 全部刪除,所有其他字元照原樣輸出。
  2. 寫一個程式,將輸入的純文字檔中連續的空格都輸出一個空格, 所有其他字元照原樣輸出。
  3. 寫一個程式,將輸入的純文字檔中每一列開頭的連續空白 (包括空格和跳格) 全部刪除,但是將不在列首的連續空白 (包括空格和跳格) 全部換成一個空格, 所有其他字元照原樣輸出。
  4. 如果你要測試這一節的程式是否足夠強軔, 請討論你該設計怎樣的輸入資料給它測試?
  5. 寫出一個簡化的 wc 程式:從 stdin 輸入純文字檔, 輸出三個數字:行數、(廣義) 字數、字元數。
[BCC16-C]
單維彰 (2000/03/30) ---
[Prev] [Next] [Up]