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

PUBLISHED ON JUN 20, 2018 — PROGRAMMING

由於初學 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:指標位址

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

標準輸出 vs. 標準錯誤

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
comments powered by Disqus