Copyright (慣C) 2018 宅色夫
data flow for the standard input (0), output (1), and error (2) streams [ source ]
在 UNIX 的設計哲學是 "Everything is a file",扣除少數的例外 (如 BSD socket),任何資源都可當作檔案來操作,不過當我們查閱 C 語言手冊和文獻時,"file" 的操作被分類在 stream I/O 中,聽起來有點奇怪吧?而 stream I/O 是 C 語言心裡最軟的一塊,這話怎説?
我們先來看 <
ctype.h>
標頭檔宣告的 islower() 函式,其作用很單純,就是判定輸入的「字元」是否為英文小寫字元,不過在宣告中,islower() 函式的參數卻是 int 型態,類似的現象也出現於 getchar() 函式的回傳值,也是 int 型態。在現行 64 位元處理器架構上運作的作業系統常用 32-bit 表示 int 型態,甚至某些作業系統用 64-bit 表示 int 型態,讓我們不禁納悶:「C 語言不是很講究效率嗎?能用 1 個 byte (char) 處理的資料,為何要用 4 個 bytes 甚至更多 (int) 去表達呢?」
簡單來說,要考慮到 EOF (end-of-file)!後者跟編譯器與執行環境有關,上述的 islower() 或 getchar() 之所以將原本 char 型態可保存的資料「擴充」為 int 型態,即是考慮到輸入和輸出的過程可能被某種情況或條件給「打斷」,倘若要深入理解更多, 就得回到 stream I/O
C++ 標準函式庫竟然有 ios::good 「iOS 真棒!」
C99 規格書 7.19.6.2 談及 fscanf
一類函式的行為, 第 5 段:
"A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read."
end-of-file 的設定是由輸入函式在讀取資料的過程中設定的。
搜尋 glibc 原始程式碼,可發現 EOF
定義在 libio/stdio.h
,但這是 Define ISO C stdio on top of C++ iostreams
/* The value returned by fgetc and similar
functions to indicate the end of the file. */
#define EOF (-1)
ctype.h
實作於 glibc 原始程式碼的 ctype/ctype.h
這裡要補充一點, 在有些系統 (特別是 UNIX 系統) 上, 每一行資料 (這裡只指 text 資料) 必須以 end-of-line 來分割, 包括最後一行. 否則, 某些程式語言在, 或系統處理最後一行時有可能會出問題。
〔資料〕
...
〔資料〕
最好是這樣:
〔資料〕
...
〔資料〕
以 ctrl-Z 做文字檔的 end-of-file marker 源自 DEC 早期的系統, 後來被 CP/M 借用, 再後來用在 MS-DOS 上。
使用 ctrl-Z 的原因是因為在早期的檔案系統,檔案長度是以 128-byte 的 sector 為單位的, 當檔案的大小不是 sector 的整數倍數的話, ctrl-Z 用來標示檔案的真正結尾, 並以 ctrl-Z 把剩餘的 sector 填滿。
自 MS-DOS 2.0 起就可以正確的記綠檔案的正確長度, 已可不需要用 ctrl-Z 的機制了. 但這個機制到目前還在支援. 因為在某個情況下它非常有用。
當你在 console 上用鍵盤來輸入資料時, 可以用 ctrl-Z 來產生 end-of-file 訊息。
〔執行〕 1 2 3 4 5 6 7 8^Z you entered 8 numbers.
UNIX 系統上用 ctrl-D 來 signal end-of-file.
以目前的系統來說, EOF 只是一個概念上的存在, 文字檔並不需要這個 marker,text editor 也不會自動的加入這個 marker.
在 MS-Windows 上, ctrl-Z 不再做為檔案大小的依據, 也不會導致 file truncation. Ctrl-Z 可以存在文字檔內, 但它會影響輸入函式的行為: 如果檔案是以 text mode 來開啟的話, ctrl-Z 會終結輸入,即使 ctrl-Z 後面還有資料也是一樣。
Standard I/O C++ iostream 的 cin
, cout
和 C stdio 的 scanf
, printf
效能比較