[TOC]
UNIX的哲學(xué),萬(wàn)物皆文件.
打開(kāi)關(guān)閉文件
FILE * fopen(const char *filename,const char * type);
系統(tǒng)調(diào)用fopen()打開(kāi)文件:給用戶(hù)指定的文件在內(nèi)存中分配一個(gè)FILE結(jié)構(gòu),并將結(jié)構(gòu)返回給用戶(hù)程序,以后用戶(hù)就可以更具FILE指針來(lái)實(shí)現(xiàn)對(duì)文件的存取操作了.當(dāng)使用打開(kāi)函數(shù)時(shí)必須給出文件名和操作方式.如果文件名不存在,就意味著創(chuàng)建文件,并將FILE *指針指向該文件.如果存在就意味著刪除該文件.
FILE 結(jié)構(gòu)體如下
typedef struct __sFILE {
unsigned char *_p; /* current position in (some) buffer */
int _r; /* read space left for getc() */
int _w; /* write space left for putc() */
short _flags; /* flags, below; this FILE is free if 0 */
short _file; /* fileno, if Unix descriptor, else -1 */
struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
int _lbfsize; /* 0 or -_bf._size, for inline putc */
/* operations */
void *_cookie; /* cookie passed to io functions */
int (* _Nullable _close)(void *);
int (* _Nullable _read) (void *, char *, int);
fpos_t (* _Nullable _seek) (void *, fpos_t, int);
int (* _Nullable _write)(void *, const char *, int);
/* separate buffer for long sequences of ungetc() */
struct __sbuf _ub; /* ungetc buffer */
struct __sFILEX *_extra; /* additions to FILE to not break ABI */
int _ur; /* saved _r when _r is counting ungetc data */
/* tricks to meet minimum requirements even when malloc() fails */
unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
unsigned char _nbuf[1]; /* guarantee a getc() buffer */
/* separate buffer for fgetln() when line crosses buffer boundary */
struct __sbuf _lb; /* buffer for fgetln() */
/* Unix stdio files get aligned to block boundaries on fseek() */
int _blksize; /* stat.st_blksize (may be != _bf._size) */
fpos_t _offset; /* current lseek offset (see WARNING) */
} FILE;
1.對(duì)于mode有常見(jiàn)的方式
| mode | description |
|---|---|
| r (read) | 以只讀方式打開(kāi)文件.該文件必須存在; |
| w (write) | 只寫(xiě)的方式.若文件存在原有的內(nèi)容會(huì)被清除;若文件不存在,創(chuàng)建文件; |
| a (append) | 追加方式打開(kāi)只寫(xiě)文件,只允許進(jìn)行寫(xiě)操作.文件存在,則添加內(nèi)容在文件末尾;文件不存在則創(chuàng)建文件 (EOF符保留) |
| + (read and write) | 可讀可寫(xiě) |
| b (binary) | 以二進(jìn)制方式打開(kāi)文件 |
| t (text) | 以文本方式打開(kāi)文件(默認(rèn)以該模式打開(kāi)文件) |
2.常見(jiàn)的組合方式
| composed mode | description |
|---|---|
| r+ | 以讀寫(xiě)的方式操作文件,允許讀寫(xiě).文件必須存在,否則返回NULL.打開(kāi)成功返回指向文件的指針,指向文件的頭部 (注意很多書(shū)上或資料上講述追加方式打開(kāi)成功后位置指針指向文件末尾是錯(cuò)誤的); |
| rb+ | 以可讀可寫(xiě),二進(jìn)制方式打開(kāi)文件,允許讀寫(xiě).文件必須存在,否則返回NULL.若打開(kāi)文件成功返回文件指針,指向文件頭部; |
| rt+ | 以可讀可寫(xiě),文本方式打開(kāi)文件,允許讀寫(xiě).文件必須存在,否則返回NULL.若打開(kāi)文件成功,返回文件指針,指向文件頭部; |
| w | 以只寫(xiě)的方式打開(kāi)文件,只允許寫(xiě),若文件存在,文件中原有內(nèi)容會(huì)被清除;若文件不存在,則創(chuàng)建文件,打開(kāi)成功后返回文件指針,位置指針指向文件頭部 |
| w+ | 以讀寫(xiě)的方式打開(kāi)文件,允許讀寫(xiě),若文件存在,文件中原有內(nèi)容會(huì)被清除;若文件不存在,則創(chuàng)建文件,打開(kāi)成功后返回文件指針,位置指針指向文件頭部 |
| a | 以追加只寫(xiě)的方式打開(kāi)文件,只允許寫(xiě).若文件存在,則追加的內(nèi)容在文件的末尾,若文件不存在則創(chuàng)建文件.打開(kāi)成功后返回文件的指針,指向文件的頭部. |
| a+ | 以追加、可讀寫(xiě)的方式打開(kāi)文件,允許讀寫(xiě)。若進(jìn)行讀操作,則從頭開(kāi)始讀;若進(jìn)行寫(xiě)操作,則將內(nèi)容添加在末尾。若文件不存在,則創(chuàng)建文件。打開(kāi)成功后返回文件指針,位置指針指向文件頭部(不保留EOF) |
二進(jìn)制和文本打開(kāi)方式基本相同,不同的地方是讀取文本是碰到ASCII碼為26的字符是,則會(huì)停止文件的讀寫(xiě),默認(rèn)文件以及結(jié)束.應(yīng)為正常的文本不會(huì)有ASCII碼26的字符.而二進(jìn)制打開(kāi)方式不存在這個(gè)問(wèn)題.(UNIX下沒(méi)有區(qū)別);
注意:
1)在以追加方式打開(kāi)文件時(shí),位置指針指向文件的首部。
? 在這里區(qū)分一下位置指針和文件指針:
? 文件指針:指向存儲(chǔ)文件信息的一個(gè)結(jié)構(gòu)體的指針
? 位置指針:對(duì)文件進(jìn)行讀寫(xiě)操作時(shí)移動(dòng)的指針
? 在頭文件<stdio.h>中存在一個(gè)結(jié)構(gòu)體_iobuf,在VC6.0中選中FILE,然后F12,則可以看到_iobuf的具體定義(UNIX 的大致相同):
struct _iobuf
{
char *_ptr; // 指向buffer中第一個(gè)未讀的字節(jié)
int _cnt; // 記錄剩余未讀字節(jié)的個(gè)數(shù)
char *_base; // 指向一個(gè)字符數(shù)組,即這個(gè)文件的緩沖
int _flag; // FILE結(jié)構(gòu)所代表的打開(kāi)文件的一些屬性
int _file; // 用于獲取文件描述,可以使用fileno函數(shù)獲得此文件的句柄。
int _charbuf; // 單字節(jié)的緩沖,即緩沖大小僅為1個(gè)字節(jié),如果為單字節(jié)緩沖,_base將無(wú)效
int _bufsiz; // 記錄這個(gè)緩沖的大小
char *_tmpfname; // temporary file (i.e., one created by tmpfile()
// call). delete, if necessary (don't have to on
// Windows NT because it was done by the system when
// the handle was closed). also, free up the heap
// block holding the pathname.
};
typedef struct _iobuf FILE;
? 比如用FILE *fp定義了一個(gè)文件指針,并成功打開(kāi)一個(gè)文件之后,fp只是指向該結(jié)構(gòu)體,而在對(duì)文件進(jìn)行讀寫(xiě)操作時(shí),fp的值并不會(huì)改變,改變的是結(jié)構(gòu)體中_ptr的值,這個(gè)_ptr就是位置指針。
? 2)以追加方式打開(kāi)時(shí),若進(jìn)行寫(xiě)操作,則rewind函數(shù)和fseek函數(shù)不會(huì)起到作用,因?yàn)橐宰芳臃绞酱蜷_(kāi)時(shí)進(jìn)行寫(xiě)操作的話(huà),系統(tǒng)會(huì)自動(dòng)將位置指針移動(dòng)到末尾。
? 3)當(dāng)文件打開(kāi)用于更新時(shí),可以通過(guò)文件指針對(duì)文件進(jìn)行讀寫(xiě)操作,但是如果沒(méi)有給出fseek或者rewind的話(huà),讀操作后面不能直接跟寫(xiě)操作,否則會(huì)是無(wú)效的寫(xiě)操作(位置指針會(huì)移動(dòng),但是需要寫(xiě)入文件的內(nèi)容不會(huì)被寫(xiě)入到文件當(dāng)中),但是寫(xiě)操作后可以直接跟讀操作。
每次調(diào)用fopen()打開(kāi)文件后都要記得調(diào)用fclose()關(guān)閉文件
C語(yǔ)言提供了以下幾種文件讀寫(xiě)方式
1.字符讀寫(xiě): fgetc()/fputc() (讀/寫(xiě));
fgetc()函數(shù):
(1)一般調(diào)用形式: char ch = fgetc(fd);
(2)作用: 文件中讀取一個(gè)字符;
(3)返回值:
? 成功:返回值所得到的字符;
? 失?。悍祷谽OF(-1)。
*注意問(wèn)的打開(kāi)方式
fputc()函數(shù):
(1)一般調(diào)用形式: char res = fputc(fd,ch);
(2)作用: 文件中寫(xiě)入一個(gè)字符;
(3)返回值
? 成功:函數(shù)輸入的字符;
? 失?。悍祷谽OF(-1)。
說(shuō)明:函數(shù)putchar()是在stdio.h中用預(yù)處理命令定義的宏,即:
//define putchar(c) fputc(c,stdout)
char a = 'a';
char ch = fgetc(fd);
char res = fputc(a, fd);
字符串讀寫(xiě): fgets()/fputs() (讀/寫(xiě));
1.fgets()函數(shù):
-
一般調(diào)用形式:char * fgets(char *str ,int num ,FILE *fd); (fd 的mode rt)
參數(shù)說(shuō)明: str 保存從文件中讀取的字符串;
? num 從文件中讀取的字符串中字符個(gè)數(shù)不超過(guò)num-1.在讀入最后一個(gè)字符后加上串結(jié)束標(biāo)志'\0'; -
返回值:
成功: 返的字符串;失敗: 返回NULL
?
2.fputs() 文件中寫(xiě)入字符串
-
一般調(diào)用形式: int num = fputs(char *s,FILE *fd); (fd 的mode at+)
s 要寫(xiě)入的字符串;
fd 待寫(xiě)入的文件;
返回值:
? 成功: 寫(xiě)入的字符個(gè)數(shù)num;
? 失敗: EOF(-1);
int res = fputs("test", fd);
char rs[11];
char *r = fgets(rs, 11, fd);
數(shù)據(jù)塊讀寫(xiě): fwrite()/fread() (讀/寫(xiě))
-
一般調(diào)用形式:
size_t fwrite(void *buffer,size_t size ,size_t count, FILE *fd);
size_t fread(void *buffer,size_t size, size_t count ,FILE *fd);
-
參數(shù)說(shuō)明:
buffer: 緩沖區(qū)指針.對(duì)fread,它是暫存讀入數(shù)據(jù)的指針;對(duì)fwrite,它是要輸出數(shù)據(jù)的指針;
size: 要讀寫(xiě)的字節(jié)數(shù);
count: 要進(jìn)行讀寫(xiě)多少個(gè)size字節(jié)的數(shù)據(jù)項(xiàng);
fd: 待讀寫(xiě)的文件指針;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct stu{
int age;
int num;
char name[10];
char addr[20];
}boya[2],boyb[2],*qq,*pp;
int main() {
FILE *fd;
char ch;
int i;
qq = boya;
pp = boyb;
if ((fd = fopen("/Users/sks/Desktop/stu_list", "wb+")) == NULL) {
printf("Cannot open file. Strike any key to exit!");
getchar();
exit(1);
}
printf("intput data\n");
for (i=0; i<2; i++,qq++) {
scanf("%d%d%s%s",&qq->age,&qq->num,qq->name,qq->addr);
}
qq = boya;
size_t writeSize = fwrite(qq, sizeof(struct stu), 2, fd);
rewind(fd);
size_t readSize = fread(pp, sizeof(struct stu), 2, fd);
printf("\n\nname\tnumber age addr\n");
for (i=0; i<2; i++,pp++) {
printf("%s %d %d %s\n",pp->name,pp->num,pp->age,pp->addr);
}
fclose(fd);
return 0;
}
本例程序定義了一個(gè)結(jié)構(gòu)stu,說(shuō)明了兩個(gè)結(jié)構(gòu)數(shù)組boya和boyb以及兩個(gè)結(jié)構(gòu)指針變量pp和qq。pp指向boya,qq指向boyb。程序第14行以讀寫(xiě)方式打開(kāi)二進(jìn)制文件“stu_list”,輸入二個(gè)學(xué)生數(shù)據(jù)之后,寫(xiě)入該文件中,然后把文件內(nèi)部位置指針移到文件首,讀出兩塊學(xué)生數(shù)據(jù)后,在屏幕上顯示。
格式化讀寫(xiě): fscanf()/fprintf() (寫(xiě)/讀);
一般調(diào)用形式:
? (1)int fprintf(FILE *stream,const char *format,[argument]…) 輸出格式化字符串或者將格式化字符串輸出到流 (文件);
? (2)int fscanf(FILE *stream, const char *) 輸入文件中的內(nèi)容到某個(gè)變量中.
? fscanf(fd,"%s",res)
返回值:
? 成功:
? fprintf讀取的字符個(gè)數(shù);
? fscanf: 返回1
? 失敗:
? EOF;
int res = fprintf(fd, "%s",s);
if (res == EOF) {
printf("輸入失敗\n");
}
char tmp[26];
//
int len = fscanf(fd, "%s\n",tmp);
?
其他文件相關(guān)操作
ftell()函數(shù): 得到流式文件的當(dāng)前讀寫(xiě)位置,返回流式文件當(dāng)前讀寫(xiě)位置距離文件頭部的字節(jié)數(shù).
long ftell(FILE *);
fseek(): 把fd 的文件讀寫(xiě)位置指針移動(dòng)到知道的位置;
@param FILE* 待操作的文件指針
@param long 距離起始點(diǎn)的位置
@parma int 計(jì)算的起始點(diǎn)
@return 0 success 文件位置指針指向正確的offset,錯(cuò)誤返回-1
/*
有三類(lèi)起始點(diǎn)
SEEK_SET 0 文件開(kāi)頭
SEEK_CUR 1 文件當(dāng)前位置
SEEK_END 2 文件末尾
*/
int fseek(FILE *, long offset, int origin);
rewind(): 將文件位置指針重新指向一個(gè)文件流的開(kāi)頭
void rewind(FILE *);
eg:
long fileLength = 0;
fseek(fd, 0, SEEK_END);
fileLength = ftell(fd);
rewind(fd);
fflush():清空緩存
所謂flush一個(gè)緩沖,是指對(duì)寫(xiě)緩沖而言,將緩沖內(nèi)的數(shù)據(jù)全部寫(xiě)入實(shí)際的文件,并將緩沖清空,這樣可以保證文件處于最新的狀態(tài)。之所以需要flush,是因?yàn)閷?xiě)緩沖使得文件處于一種不同步的狀態(tài),邏輯上一些數(shù)據(jù)已經(jīng)寫(xiě)入了文件,但實(shí)際上這些數(shù)據(jù)仍然在緩沖中,如果此時(shí)程序意外地退出(發(fā)生異?;驍嚯姷龋?,那么緩沖里的數(shù)據(jù)將沒(méi)有機(jī)會(huì)寫(xiě)入文件。flush可以在一定程度上避免這樣的情況發(fā)生。
在這個(gè)表中我們還能看到C語(yǔ)言支持兩種緩沖,即行緩沖(Line Buffer)和全緩沖(Full Buffer)。全緩沖是經(jīng)典的緩沖形式,除了用戶(hù)手動(dòng)調(diào)用fflush外,僅當(dāng)緩沖滿(mǎn)的時(shí)候,緩沖才會(huì)被自動(dòng)flush掉。而行緩沖則比較特殊,這種緩沖僅用于文本文件,在輸入輸出遇到一個(gè)換行符時(shí),緩沖就會(huì)被自動(dòng)flush,因此叫行緩沖。
FILE *fd;
if ((fd = fopen("/Users/sks/Desktop/ch.txt", "w")) == NULL) {
printf("Open file failed.Press any key to exit!");
getchar();
exit(1);
}
Student stu;
stu.number = 10000;
// stu.name = "guohuabing";
strcpy(stu.name, "guohuabing");
fflush(fd);
fwrite(&stu, sizeof(struct SStudent), 1, fd);
fclose(fd);
socket 通信和文件操作的關(guān)系
待續(xù)....
C語(yǔ)言文件操作和操作系統(tǒng)文件子系統(tǒng)的關(guān)系
參考:
? http://www.2cto.com/kf/201207/143344.html
? http://www.cnblogs.com/L-hq815/archive/2012/06/30/2571066.html
? http://www.cnblogs.com/dolphin0520/archive/2011/10/05/2199598.html