順序讀寫文件
文件打開后可以對其進(jìn)行讀寫操作,打開時(shí)需要指定相應(yīng)的打開模式)。讀寫操作分為順序讀寫操作和隨機(jī)讀寫操作。順序讀寫操作是指打開文件之后從文件頭開始讀寫,即讀寫數(shù)據(jù)的順序和數(shù)據(jù)在文件中的存儲順序是一致的。
讀寫單個(gè)字符
從文件讀取單個(gè)字符可以使用fgetc和getc函數(shù)。這兩個(gè)函數(shù)的作用都是從文件流中讀取下一個(gè)字符并推進(jìn)文件的位置指示器(用來指示要讀寫的下一一個(gè)字符的位置)。
#include <stdio.h>
...
int fgetc (FILE *stream) ;
int getc(FILE *stream) ;
參數(shù)是一個(gè)FILE結(jié)構(gòu)的指針,指定一個(gè)待讀取的文件流。如果讀取成功,該函數(shù)將讀取到的unsigned char 類型轉(zhuǎn)換為int類型并返回:如果文件結(jié)束或者遇到錯(cuò)誤則返回EOF。
fgetc和getc 的功能和描述基本上是一樣的, 它們的區(qū)別主要在于實(shí)現(xiàn): fgetc 是個(gè)函數(shù):而getc實(shí)際上是一個(gè)宏的實(shí)現(xiàn)。一殷來說,宏雖產(chǎn)生較大的代碼量,但是避免了函數(shù)調(diào)用的堆棧操作,所以速度會(huì)比較快。由于getc 是由宏實(shí)現(xiàn)的,對其參數(shù)可能有不止一次的調(diào)用,所以不能使用帶有副作用(side efects)的參數(shù)(所謂副作用,其實(shí)就是參數(shù)中不能存在自增、自減運(yùn)算符,因?yàn)楹暝谡归_后可能參數(shù)會(huì)被多次引用。
向文件寫入單個(gè)字符可以使用fputc和pute函數(shù)。
#include <stdio.h>
...
int fputc(int c, FILE *stream) ;
int putc(int CFILE *stream) ;
與上面一樣,它們的區(qū)別是: fputce 是個(gè)函數(shù), 而pute則是 個(gè)宏的實(shí)現(xiàn)。 它們都有兩個(gè)參數(shù):第一個(gè)參數(shù)指定待寫入的字符:第二個(gè)參數(shù)是一個(gè)FILE結(jié)構(gòu)的指針,指定一個(gè)待寫入的文件流。


讀寫整個(gè)字符串
從文件讀整個(gè)字符串和向文件寫整個(gè)字符串可以使用fgets 和fputs 函數(shù)。
#include <stdio.h>
...
char *fgets (char *S,int size, FILE *stream) ;
int fputs (const char *s, FILE *stream) ;
其中,fgets 函數(shù)用于從指定文件中讀取字符串。fgets 函數(shù)最多可以讀取size -1個(gè)字符,因?yàn)榻Y(jié)尾處會(huì)自動(dòng)添加個(gè)字符串結(jié)束符 (^0)。當(dāng)讀取到換行符 ("n')或文件結(jié)束符(EOF)時(shí),表示結(jié)束讀取("n'會(huì)被作為個(gè)合法的字符讀取)。
fgets函數(shù)有三個(gè)參數(shù):第一個(gè)參數(shù)是一個(gè)字符型指針,指向用于存放讀取到的字符串的位置;第二個(gè)參數(shù)指定讀取的字符數(shù)(包括最后自動(dòng)添加的"0):最后一個(gè)參數(shù)是一個(gè)FILE結(jié)構(gòu)的指針,指定一個(gè)待讀取的文件流。
如果函數(shù)調(diào)用成功,返回第一個(gè)參數(shù)指向的地址。如果在讀取字符的過程中遇到EOF,則eof指示器被設(shè)置:如果還沒讀入任何字符就遇到EOF,則第一個(gè)參數(shù)指向的位置保持原來的內(nèi)容,函數(shù)返回NULL;如果在讀取的過程中發(fā)生錯(cuò)誤,則error指示器被設(shè)置,函數(shù)返回NULL,但第一個(gè)參數(shù)指向的內(nèi)容可能被改變。
將一個(gè)字符串寫入文件中使用fpus 函數(shù)。注意,字符串結(jié)束符("0)不會(huì)被一并寫入。
fputs函數(shù)有兩個(gè)參數(shù):第一個(gè)參數(shù)是一個(gè)字符型指針,指向用于存放待寫入字符串的位置:第二個(gè)參數(shù)是一個(gè)FILE結(jié)構(gòu)的指針,指定一個(gè)待操作的數(shù)據(jù)流。如果函數(shù)調(diào)用成功,返回一個(gè)非0值:如果函數(shù)調(diào)用失敗,返回EOF。
舉個(gè)例子調(diào)用多次fpus函數(shù)將幾個(gè)字符串寫入文件中,然后調(diào)用fgets 函數(shù)讀取并打印到屏幕上


feof函數(shù)用于檢測eof指示器是否被設(shè)置,如果檢測到文件末尾指示器被設(shè)置,返回一個(gè)非0值:否則返回0。
第三行字符串打印了兩次,但是查看lines.txt 文件的內(nèi)容卻是正確的:

分析:這是因?yàn)閒gets 函數(shù)一旦遇到換行符("n')就會(huì)停止本次字符串的讀取,而在最后一行字符串讀取完成之后,并沒有遇到EOF (是換行符導(dǎo)致了本次讀取結(jié)束),所以它還要多讀取一次,但是這一次除了讀取到EOF就沒有其他內(nèi)容了,因此buffer字符串的內(nèi)容并沒有被新的內(nèi)容所覆蓋。
格式化讀寫文件
scanf和printf函數(shù)使用它們可以對終端進(jìn)行格式化字符串輸入和輸出。同樣,對于文件的讀寫,也有兩個(gè)類似的函數(shù)— fscanf 和fprintf函數(shù)。
#include <stdio.h>
...
int fscanf (FILE *stream, const char *format, .. ) ;
int fprintf (FILE *stream, const char*format, .. .) ;
從名字上看,它們只是在scanf和printf前面加上一個(gè)表示文件的f (file); 從用法上看,fscanf 和fprintf函數(shù)的讀寫對象是文件而不是終端,其中的format 參數(shù)和附加參數(shù)用法與scanf和printf函數(shù)是一致的。


二進(jìn)制讀寫文件
雖然fopen函數(shù)可以指定以何種模式(文本模式或二進(jìn)制模式)打開一個(gè)文件,但這并不意味著后續(xù)對該文件的操作就定是對應(yīng)的形式。


代碼中,雖然使用"wb"二進(jìn)制模式打開了一個(gè)text.txt文件,但仍然調(diào)用fputc函數(shù)成功地將四個(gè)字符('51、12'、 '0'和\n') 寫入了文件中。使用xxd命令以十六進(jìn)制的形式查看該文件內(nèi)容,35、32、30和0a分別對應(yīng)的是'5'、'2'、 '0'和"\n'的 ASCII編碼。
C語言為直接讀寫文件提供了fread 和fwrite函數(shù)。
#include <stdio.h>
size_t fread(void*ptr, size_t size,size_t nmemb, FILE *stream) ;
size_t fwrite (const void *ptr,size_t size,size_t nmemb, FILE *stream);
其中,fread 函數(shù)用于從指定文件中讀取指定尺寸的數(shù)據(jù)。該函數(shù)共有四個(gè)參數(shù):第一個(gè)參數(shù)指向存放數(shù)據(jù)的內(nèi)存塊地址:第二個(gè)參數(shù)指定待讀取的每個(gè)元素的尺寸:第三個(gè)參數(shù)指定待讀取的元素個(gè)數(shù):最后一個(gè)參數(shù)是一個(gè)FILE結(jié)構(gòu)的指針,指向一個(gè)待讀取的文件流。



既然是使用文本模式打開的文件,又能夠正常地將內(nèi)容打印到屏幕上,那么file.xt文件中的內(nèi)容定應(yīng)該是文本。 使用vi命令打開file.xt文件來看看

這是典型的二進(jìn)制數(shù)據(jù),因此使用文本編輯器并不能讀取其內(nèi)容。
不難得出結(jié)論:不管是用文本模式還是用二進(jìn)制模式打開文件,都不能決定寫入數(shù)據(jù)的形式,它們只是影響換行符的表現(xiàn)形式而已。真正決定數(shù)據(jù)是以字符的形式寫入還是以二進(jìn)制的形式寫入,則是相關(guān)的文件讀寫函數(shù)。