?? 第4章 數據文件.txt
字號:
C語言編程常見問題解答
發表日期:2003年9月25日 已經有2341位讀者讀過此文
第4章 數據文件
本章重點討論C語言的強大功能之一 —— 磁盤輸入和輸出。多年來,最快、最簡單的專業程序都是用C語言編寫的,并且受益于C語言優化了的文件I/O程序。
處理數據文件有時是比較困難的,本章將綜合分析這方面的一些常見問題,例如流(stream)、文件模式(文本(text)和二進制(binary))以及文件和目錄的處理等。目前,大多數專業程序是面向網絡的,因此本章末尾討論了有關文件共享和一致性控制的一些問題,希望讀者認真閱讀。此外,本章也討論了許多與文件有關的問題,例如DOS中的文件句柄和硬件錯誤處理程序的安裝。
4. 1 當errno為一個非零值時,是否有錯誤發生?
許多標準的C庫函數都通過全局變量errno向程序傳遞一個錯誤號,以表明發生哪種錯誤,但是,你的程序不應該通過檢查errno的值來判斷是否發生了錯誤。通常,被調用的標準的C庫函數都有一個返回值,該值將表示是否發生了錯誤,并且表示是否已給errno賦予了相應的錯誤號。在沒有發生錯誤或所調用的函數不使用errno時,在errno中很可能仍然保留著一個錯誤號。有時,為了改善運行速度,使用errno的函數并不將errno清零。
總之,絕對不能單憑errno的值來判斷是否發生了錯誤,而應該根據函數的返回值來判斷是否應該檢查errno的值。請參考你所使用的編譯程序的有關文檔,看看哪些函數使用了errno全局變量,以及errno的有效值清單。
4. 2 什么是流(stream)?
流是程序輸入或輸出的一個連續的字節序列,設備(例如鼠標、鍵盤、磁盤、屏幕、調制解調器和打印機)的輸入和輸出都是用流來處理的。在C語言中,所有的流均以文件的形式出現----不一定是物理磁盤文件,還可以是對應于某個輸入/輸出源的邏輯文件。C語言提供了5種標準的流,你的程序在任何時候都可以使用它們,并且不必打開或關閉它們。以下列出了這5種標準的流。
------------------------------------------------
名稱 描 述 例 子
------------------------------------------------
stdin 標準輸入 鍵盤
stdout 標準輸出 屏幕
stderr 標準錯誤 屏幕
stdprn 標準打印機 LPT1端口
stdaux 標準串行設備 COM1端口
------------------------------------------------
需要注意的是,stdprn和stdaux并不總是預先定義好的,因為LPT1和COM1端口在某些操作系統中是沒有意義的,而stdin,stdout和stderr總是預先定義好的。此外,stdin并不一定來自鍵盤,stdout也并不一定顯示在屏幕上,它們都可以重定向到磁盤文件或其它設備上。
請參見:
4.3 怎樣重定向一個標準流?
4.4 怎樣恢復一個重定向了的標準流?
4.5 stdout能被強制打印到非屏幕設備上嗎?
4.3 怎樣重定向一個標準流?
包括DOS在內的大多數操作系統,都提供了將程序的輸入和輸出重定向到不同設備上的手段。這就是說,程序的輸出并不一定是到屏幕上,還可以重定向到文件或打印機端口上;程序的輸入并不一定來自鍵盤,還可以重定向到文件上。
在DOS中,重定向是通過重定向字符“<”和“>”來實現的。例如,如果你要求程序PRINTIT.EXE的輸入來自文件STRINGS.TXT,你就可以在DOS提示符下鍵入如下命令:
C:\>PRINTIT<STRINGS.TXT
請注意,可執行文件的名稱總是第一個出現。“<”符號告訴DOS將STRINGS.TXT中的字符串作為程序PRINTIT.EXE的輸入。關于重定向stdout標準流的例子請看4. 5。
標準流的重定向并不一定總在操作系統下進行,在程序內部,用標準C庫函數freopen()同樣可以重定向標準流。例如,如果你要求在程序內部將標準流stdout重定向到文件OUTPUT.TXT,你就可以象下面這樣使用freopen()函數:
freopen("output.txt","w",stdout);
現在,程序中每條輸出語句(例如prinft();puts(),putch()等)輸出的內容都將出現在文件OUTPUT.TXT中。
請參見:
4.2 什么是流(stream)?
4.4 怎樣恢復一個重定向了的標準流?
4.5 stdout能被強制打印到非屏幕設備上嗎?
4.4 怎樣恢復一個重定向了的標準流?
4.3中的例子演示了如何在程序內部重定向標準流。如果要將重定向了的標準流恢復到初始狀態,可以使用標準C庫函數dup()和fdopen()。
dup()函數可以復制一個文件句柄,你可以用dup()函數保存對應于stdout標準流的文件句柄。fdopen()函數可以打開一個已用dup()函數復制了的流。這樣,你就可以重定向并恢復標準流,請看下例:
#include <stdio.h>
void main(void);
void main(void)
{
int orig-stdout;
/ * Duplicate the stdout file handle and store it in orig_stdout. */
orig_stdout = dup (fileno (stdout));
/ * This text appears on-screen. * /
printf("Writing to original stdout... \n") ;
/ * Reopen stdout and redirect it to the "redir. txt" file. * /
freopen("redir.txt", "w", stdout);
/ * This text appears in the "redir. txt" file. * /
printf("Writing to redirected stdout.., \n");
/* Close the redirected stdout. * /
fclose (stdout);
/ * Restore the original stdout and print to the screen again. * /
fdopen(orig_stdout, "w" );
printf("I'm back writing to the original stdout. \n");
}
4.2 什么是流(stream)?
4.3 怎樣重定向一個標準流?
4. 5 stdout能被強制打印到非屏幕設備上嗎?
4. 5 stdout能被強制打印到非屏幕設備上嗎?
盡管標準流stdout的缺省方式是打印在屏幕上,但你可以將它重定向到其它設備上。請看下面的例子:
/* redir.c */
#include<stdio.h>
void main(void);
void main(void)
{
printf(”Let's get redirectedI\n”),
}
在DOS提示符下,通過重定向字符“>”,可以將上例對應的可執行程序的輸出重定向到非屏幕設備上。例如,下例將該程序的輸出重定向到prn設備(通常就是連接到LPTl端口的打印機)上:
C:\>REDIR>PRN
同樣,你也可以將該程序的輸出重定向到一個文件上,請看下例:
C:\>REDIR>REDIR.OUT
在上例中,原來在屏幕上顯示的輸出內容將全部寫入文件REDIR.OUT中。
請參見:
4.2什么是流(stream)?
4.3怎樣重定向一個標準流?
4.4怎樣恢復一個重定向了的標準流?
4. 6 文本模式(textmode)和二進制模式(binarymode)有什么區別?
流可以分為兩種類型:文本流和二進制流。文本流是解釋性的,最長可達255個字符,其中回車/換行將被轉換為換行符“\n”,反之亦然。二進制流是非解釋性的,一次處理一個字符,并且不轉換字符。
通常,文本流用來讀寫標準的文本文件,或者將字符輸出到屏幕或打印機,或者接受鍵盤的輸入;而二進制流用來讀寫二進制文件(例如圖形或字處理文檔),或者讀取鼠標輸入,或者讀寫調制解調器。
請參見:
4.18怎樣讀寫以逗號分界的文本?
4.7 怎樣判斷是使用流函數還是使用低級函數?
流函數(如fread()和fwrite())帶緩沖區,在讀寫文本或二進制文件時效率更高。因此,一般來說,使用流函數比使用不帶緩沖區的低級函數(如read()和write())會使程序性能更好。
然而,在多用戶環境中,文件需要共享,文件中的一部分會不斷地被加鎖、讀、寫或解鎖,這時流函數的性能就不如低級函數好,因為共享文件的內容變化頻繁,很難對它進行緩沖。因此,通常用帶緩沖區的流函數存取非共享文件,用低級函數存取共享文件。
4.8 怎樣列出某個目錄下的文件?
C語言本身沒有提供象dir_list()這樣的函數來列出某個目錄下所有的文件。不過,利用C語言的幾個目錄函數,你可以自己編寫一個dir_list()函數。
首先,頭文件dos.h定義了一個find_t結構,它可以描述DOS下的文件信息,包括文件名、時間、日期、大小和屬性。其次,C編譯程序庫中有_dos_findfirst()和_dos_findnext()這樣兩個函數,利用它們可以找到某個目錄下符合查找要求的第一個或下一個文件。
dos_findfirst()函數有三個參數,第一個參數指明要查找的文件名,例如你可以用“*.*”指明要查找某個目錄下的所有文件。第二個參數指明要查找的文件屬性,例如你可以指明只查找隱含文件或子目錄。第三個參數是指向一個find_t變量的指針,查找到的文件的有關信息將存放到該變量中。
dos_findnext()函數在相應的目錄中繼續查找由_dos_findfirst()函數的第一個參數指明的文件。_dos_findnext()函數只有一個參數,它同樣是指向一個find_t變量的指針,查找到剛文件的有關信息同樣將存放到該變量中。
利用上述兩個函數和find_t結構,你就可以遍歷磁盤上的某個目錄,并列出該目錄下所有的文件,請看下例:
#include <stdio.h>
#include <direct.h>
#include <dos.h>
#include <malloc.h>
#include <memory.h>
#include <string.h>
typedef struct find_t FILE_BLOCK
void main(void);
void main(void)
{
FILE_BLOCK f-block; /* Define the find_t structure variable * /
int ret_code; / * Define a variable to store the return codes * /
/ * Use the "*.*" file mask and the 0xFF attribute mask to list
all files in the directory, including system files, hidden
files, and subdirectory names. * /
ret_code = _dos_findfirst(" *. * ", 0xFF, &f_block);
/* The _dos_findfirst() function returns a 0 when it is successful
and has found a valid filename in the directory. * /
while (ret_code == 0)
{
/* Print the file's name * /
printf(" %-12s\n, f_block, name);
/ * Use the -dos_findnext() function to look
for the next file in the directory. * /
ret_code = _dos_findnext (&f_block);
}
printf("\nEnd of directory listing. \n" );
}
請參見:
4. 9 怎樣列出一個文件的日期和時間?
4. 10 怎樣對某個目錄下的文件名進行排序?
4. 11 怎樣判斷一個文件的屬性?
4. 9 怎樣列出一個文件的日期和時間?
在_dos_findirst()和_dos_findfnext()函數所返回的find_t結構中(請參見4.8),存放著查找到的文件的日期和時間,因此,只要對4.8中的例子稍作改動,就可以列出每個文件的日
期、時間和文件名。
文件的日期和時間存放在結構成員find_t.wr_date和find_t.wr_time中。文件的時間存放在一個雙字節的無符號整數中,見下表:
-------------------------------------------------------------
元 素 位域大小 取值范圍
-------------------------------------------------------------
秒 5位 0—29(乘以2后為秒值)
分 6位 0—59
時 5位 0—23
-------------------------------------------------------------
文件的日期同樣也存放在一個雙字節的無符號整數中,見下表:
-------------------------------------------------------------
元 素 位域大小 取值范圍
-------------------------------------------------------------
日 5位 1—31
月 4位 1—12
年 7位 1--127(加上1980后為年值)
-------------------------------------------------------------
因為DOS存儲文件的秒數的間隔為兩秒,所以只需使用0--29這個范圍內的值。此外,DOS產生于1980年,因此文件的日期不可能早于1980年,你必須加上“1980”這個值才能得到真正的年值。
以下是列出某個目錄下所有的文件及其日期和時間的一個例子:
#include <stdio.h>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -