C 語言程式設計教學:格式化字串輸出入

PUBLISHED ON JUN 20, 2018 — PROGRAMMING
FacebookTwitter LinkedIn LINE Skype EverNote GMail Yahoo Email

    由於初學 C 程式設計時都在撰寫終端機程式,學習格式化字串輸出入有助於我們和程式進行互動。

    assert 取代 printf

    很多 C 程式設計的教材都會在早期就介紹格式化字串輸出,其中一個目的是將資料印在終端機上,讓學習者可以有回饋;筆者一開始也是這樣做,但筆者後來很少用格式化字串輸出來檢查程式碼了。因為我們可以用 assert 敘述來取代 printf,像是以下例子:

    #include <stdio.h>
    
    int main(void) {
        int n = 3 + 2;
        
        printf("%d\n", n);
        
        return 0;
    }
    

    我們要把程式執行後,再由人工觀看終端機的輸出,才能確認 n 是否符合我們的預期。但我們將程式改寫如下:

    #include <assert.h>
    
    int main(void) {
        int n = 3 + 2;
        
        assert(n == 5);
        
        return 0;
    }
    

    在我們這裡例子中,只要程式可以順利執行,即代表程式符合我們的預期,不需要人工逐一確認。此外,我們在閱讀程式碼時,可以清楚地知道我們檢查的標的。

    不過,我們有時候還是會用到格式化字串,因此,我們接下來會介紹相關內容。

    格式化輸出入的 format

    我們以 printf 為例來說明 format 如何撰寫。printf 函式的宣告如下 (可參考這裡):

    int printf ( const char * format, ... );
    

    printf 中,第一個參數是 *format*,第二個以後參數的數量和型別會和 format 相關;使用格式化輸出的重點是學習撰寫 *format*。

    以下是一個簡例:

    printf("%s %s\n", "Hello", "Michael");
    // Out: Hello Michael
    

    在這個例子中,format"%s %s\n",該 format 接收兩個字串 (%s),並在字串尾部加上一個換行符號。

    以下是另一個簡例:

    printf("3 + 2 = %d\n", (3 + 2));
    // Out: 3 + 2 = 5
    

    在這個例子中,format"3 + 2 = %d\n",該 format 接收一個整數 (%d),並在字串尾部加上一個換行符號。

    基本上,format 除了塞入值的地方,其他的地方都是固定的字串,我們只要學習一些特殊的表示法即可。常見的特殊字串如下:

    • %d%i:整數
    • %f:浮點數
    • %c:字元
    • %s:字串 (即字元陣列)
    • %p:指標位址

    其他更詳細的用法可參考前述的參考資料。

    標準輸出和標準錯誤

    fprintf 可指定輸出的目標,其宣告如下 (可參考這裡):

    int fprintf ( FILE * stream, const char * format, ... );
    

    format 的寫法和先前的介紹相同。但 fprintf 可指定輸出標的。一個例子是將錯誤訊息輸出到標準錯誤 (standard error):

    fprintf(stderr, "Something wrong\n");
    

    命令列程式印出訊息其實有兩個標的,一個是標準輸出 (stdout),一個是標準錯誤 (stderr),表面上看起來兩種標的都是印在終端機上,但其實兩者是相異的標的。如果對命令列程式的輸出進行重導 (redirecting) 就會發現其差異。

    以下是範例程式碼:

    #include <stdio.h>
    
    int main()
    {
        // Go to stdout.
        printf("Some information\n");
        
        // Go to stderr.
        fprintf(stderr, "Something wrong\n");
    
        return 0;
    }
    

    執行該程式:

    # First run.
    $ ./program
    Some information
    Something wrong
    
    # Second run.
    $ ./program 1>/dev/null
    Something wrong
    

    第一次執行程式時,標準輸出和標準錯誤皆有文字訊息。在第二次執行程式時,我們將標準輸出重導走,就只留下標準錯誤。對於一般訊息和錯誤訊息的輸出,應用兩種不同的標的,程式使用者才能將其分開來。

    格式化輸入

    除了格式化輸出,我們也會使用格式化輸入,這是為了製作在終端機互動的程式。C 語言常用 scanf 進行格式化輸入,scanf 的宣告如下 (參考這裡):

    int scanf ( const char * format, ... );
    

    由於輸入時有可能會得到錯誤的值,需檢查是否輸入成功。一個例子如下:

    #include <stdio.h>
    
    int main()
    {
        printf("Input a number: ");
        
        int n;
        if (scanf("%d", &n) == 1) {
            printf("You input %d\n", n);
        } else {
            fprintf(stderr, "Invalid input\n");
        }
    
        return 0;
    }
    

    &n 這個部分令人有點困惑,這代表變數 n 的記憶體位址。這牽涉到指標操作,現在就當成固定的寫法即可。scanf("%d", &n) == 1 檢查 scanf 的回傳值是否為 1,在本例中,我們僅輸入一個變數,回傳 1 即代表輸入成功。

    如果輸入正確,可顯示輸入的值:

    $ ./program
    Input a number: 36
    You input 36
    

    如果輸入錯誤,會吐出錯誤訊息:

    $ ./program
    Input a number: something
    Invalid input