網頁

2009年9月3日 星期四

C Language I/O

http://www.eefocus.com/html/09-05/5311327060403cJqm.shtml



除了人工的分析之外,最簡單最直接的調試方法要算printf了。不過,我們這裡推薦使用的並不是初學C語言時使用的函數int printf(const char *format, ...),而是稍微複雜一點的fprintf()函數,因為它更方便我們之後重定向錯誤輸出信息到指定的設備。 fprintf()函數的原型如下:
int fprintf(FILE *stream, const char *format, ...)

可以看到,它與printf()函數相比多出來了第一個參數FILE *stream,其意義是將打印的內容輸出到文件流指針stream指向的流。所謂流,通常是指程序輸入或輸出的一個連續的字節序列,設備(例如鼠標、鍵盤、磁盤、屏幕、調製解調器和打印機)的輸入和輸出都是用流來處理的,在C語言中,所有的流均以文件的形式出現——不一定是物理磁盤文件,還可以是對應於某個輸入/輸出源的邏輯文件。 C語言提供了5種標準的流,你的程序在任何時候都可以使用它們,並且不必打開或關閉它們。以下列出了這5種標準的流。
----------------------------------------
名稱 描述 例子
stdin 標準輸入 鍵盤
stdout 標準輸出 屏幕
stderr 標準錯誤 屏幕
stdprn 標準打印機 LPT1端口
stdaux 標準串行設備 COM1端口
----------------------------------------



其中,stdprn和stdaux並不總是預先定義好的,因為LPT1和COM1端口在某些操作系統中是沒有意義的,而stdin,stdout和stderr總是預先定義好的。此外,stdin並不一定來自鍵盤,stdout也並不一定顯示在屏幕上,它們都可以重定向到磁盤文件或其它設備上。我們在頭文件stdio.h中可以找到stdin,stdout和stderr的定義如下:
/* Standard streams. */
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
extern struct _IO_FILE *stderr; /* Standard error output stream. */


在使用fprintf()函數時,通常我們可以將第一個參數設為stdout或者stderr,打印出錯調試信息的時候則推薦使用stderr而不是stdout,這是一種慣例,同時也由於內核在處理stdout和stderr時的優先級不一樣,後者的優先級要高一些,因此有時候如果程序異常退出時,stderr能得到輸出,而stdout就不行。
printf(...)實際上相當於fprintf(stdout, ...),這也是為什麼我們不推薦使用它的原因。在輸出調試信息的時候,我們推薦使用fprintf(stderr, …),或者使用某個指定的文件流fprintf(some_stream, …)。

那麼具體如何在必要的時候重定向fprintf()中的調試信息呢?來看看下面的一些方法:
當調試信息的量比較大,需要一些時間或者其他輔助工具來搜索過濾時,僅僅利用顯示屏幕來輸出調試信息是不夠的,這時我們經常將這些信息輸出到所謂的日誌文件(log)中,之後再仔細的分析log文件來發現問題。
利用Shell的I/O重定向
簡單的寫log方法可以通過shell的I/O重定向機制來實現,比如下面的代碼:
1 #include
2
3 int main()
4 {
5 fprintf(stdout, "This is a standard output info!\n");
6 fprintf(stderr, "This is a standard error output info!\n");
7 return 0;
8 }


在默認條件下,編譯運行的結果是打印信息都輸出在屏幕上:
$ gcc fprint.c -o fprint
$ ./fprint
This is a standard output info!
This is a standard error output info!


這是因為默認情況下,shell所打開的stdout和stderr設備都是顯示屏幕。不過我們可以通過shell的重定向功能來將打印信息寫到文件中去。比如:
$ ./fprint >output.log
This is a standard error output info!
$ cat output.log
This is a standard output info!


這樣,我們把stdout的輸出寫到了文件output.log中,不過stderr的輸出還是在屏幕上。如何重定向stderr呢?這需要用到shell定義的文件描述符。在shell下stdin, stdout,和stderr的文件描述符分別是0, 1和2,我們可以用下面的方法重定向:

$ ./fprint >output.log 2>error.log
$ cat output.log
This is a standard output info!
$ cat error.log
This is a standard error output info!
$
$ ./fprint >output.log 2>&1
$ cat output.log
This is a standard error output info!
This is a standard output info!

其中./fprint >output.log 2>error.log分別將stdout和stderr的輸出寫入到文件output.log和error.log中,而./fprint >output.log 2>&1則表示將stderr的輸出追加到stdout的文件output.log中(結果是output.log中既有stdout輸出也有stderr輸出)。

一些常用的shell I/O語法如下:
cmd > file把stdout重定向到file文件中
cmd >> file把stdout重定向到file文件中(追加)
cmd 1> fiel把stdout重定向到file文件中
cmd > file 2>&1把stdout和stderr一起重定向到file文件中
cmd 2> file把stderr重定向到file文件中
cmd 2>> file把stderr重定向到file文件中(追加)
cmd >> file 2>&1把stderr和stderr一起重定向到file文件中(追加)

在平時的簡單調試中,我們可以靈活利用這些方法來快速得到log文件。

用freopen()進行重定向
有時候我們要求在程序中能夠控制標準流的重定向,這時可以利用標準C庫函數freopen()。 freopen()的函數原型如下:
FILE *freopen(const char *filename, const char *mode, FILE *stream)
下面的代碼用來測試用函數freopen()重定向stderr:

1 #include
2
3 int main()
4 {
5 if (freopen("err.log", "w", stderr)==NULL)
6 fprintf(stderr, "error redirecting stderr\n");
7 fprintf(stdout, "This is a standard output info!\n");
8 fprintf(stderr, "This is a standard error output info!\n");
9 fclose(stderr);
10 return 0;
11 }

在第5行我們用freopen()函數將stderr重定向到了”err.log”文件,這樣得到的結果如下:
$ gcc print_log.c -o print_log
$ ./print_log
This is a standard output info!
$ cat err.log
This is a standard error output info!


可見第8行打印到stderr的信息被重定向到了err.log文件中,而第7行stdout的打印信息則還是輸出到了屏幕上。

沒有留言: