1.5 輸入和輸出
文件描述符
文件描述符通常是小的且非負的整數,內核使用它來標識能被特定進程訪問的文件。只要打開或創建了新文件,內核就返回一個文件描述符,我們使用它來讀寫文件。
標準輸入,標準輸出和標準錯誤
通常,一旦一個新的程序運行,所有的shell就為其打開三個描述符:標準輸入,標準輸出和標準錯誤。如果不做什么特別的事,就像下面這樣一條簡單的命令:
ls
那么所有的三個描述符都連接到終端。多數shell提供了一種方式來重定向三個描述符到文件。例如:
ls > file.list
執行ls命令,并把它的標準輸出重定向到名為file.list的文件。
無緩沖I/O
無緩沖I/O由open函數,read函數,write函數,lseek函數和close函數提供。這些函數都需要文件描述符才能工作。
例子
如果我們想要從標準輸入讀取,從標準輸出寫入,那么圖1.4中的程序就可以拷貝UNIX系統中的常規文件。
apue.h中包含的<unistd.h>頭文件,以及兩個常量STDIN_FILENO和STDOUT_FILENO都是POSIX標準(下章中將會詳細介紹)的一部分。在該頭文件中有許多UNIX系統服務的函數的原型,例如我們調用的read和write函數。
常量STDIN_FILENO和STDOUT_FILENO在<unistd.h>中定義,它們指定了標準輸入和標準輸出的文件描述符。通常,STDIN_FILENO和STDOUT_FILENO的值分別為0和1,然而,在便攜設備開發中,我們使用新的名字來表示它們。
3.9節中會詳細討論BUFFSIZE常量,將看到它的不同值是如何影響一個程序的效率的。盡管如此,無論BUFFSIZE為何值,圖1.4中的程序都會拷貝常規文件。
read函數返回讀取的字節數,這個值被用來作為要寫入的字節數。當到達輸入文件的末尾時,read函數返回0,程序停止。如果讀取時發生錯誤,read函數返回-1。多數系統函數在發生錯誤時返回1。
如果像下面這樣按標準名字(a.out)編譯我們的程序
./a.out > data
標準輸入就是終端,而標準輸出被重定向到文件data,同時標準錯誤也是終端。如果這個輸出文件不存在,那么shell將默認創建它。除非輸入終止符(通常是Control-D),程序將拷貝輸入到標準輸出。
如果我們運行
./a.out < infile > outfile
那么文件finfile將被拷貝到文件outfile。

圖 1.4 列出目錄中的所有文件
第三章中將詳細描述無緩沖I/O函數。
標準I/O
標準I/O函數為無緩沖I/O函數提供了緩沖接口。使用標準I/O可以防止在選擇合適緩沖大小的時候出現錯誤,例如圖1.4中的BUFFSIZE常量。使用標準I/O函數的另一個優點是其僅僅處理輸入的行(UNIX應用程序中的通常事件)。例如fgets函數,它讀取整個一行。另一方面,read函數讀取指定的字節數。就像將5.4節中看到的那樣,標準I/O庫提供的函數讓我們能夠控制緩沖的使用風格。
最常見的標準I/O函數就是printf。如果在程序中調用printf函數,可以通過包含apue.h來包含<stdio.h>頭文件,<stdio.h>頭文件中包含了所有標準I/O函數的原型。
例子
圖1.5中的程序會在5.8節中詳細討論。該程序像前面的程序那樣調用read何write函數。該程序拷貝標準輸入到標準輸出,同時也能夠拷貝任何常規文件。
getc函數一次讀取一個字符,該字符被putc函數寫入。在最后一個輸入的字節被讀取后,getc返回常量EOF(<stdio.h>中定義)。標準I/O常量stdin和stdout也在<stdio.h>頭文件中定義,它們分別表示標準輸入和標準輸出。

圖 1.5 使用標準I/O拷貝標注輸入到標注輸出