[TOC]
UNIX的哲學,萬物皆文件.
打開關閉文件
FILE * fopen(const char *filename,const char * type);
系統調用fopen()打開文件:給用戶指定的文件在內存中分配一個FILE結構,并將結構返回給用戶程序,以后用戶就可以更具FILE指針來實現對文件的存取操作了.當使用打開函數時必須給出文件名和操作方式.如果文件名不存在,就意味著創建文件,并將FILE *指針指向該文件.如果存在就意味著刪除該文件.
FILE 結構體如下
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.對于mode有常見的方式
mode | description |
---|---|
r (read) | 以只讀方式打開文件.該文件必須存在; |
w (write) | 只寫的方式.若文件存在原有的內容會被清除;若文件不存在,創建文件; |
a (append) | 追加方式打開只寫文件,只允許進行寫操作.文件存在,則添加內容在文件末尾;文件不存在則創建文件 (EOF符保留) |
+ (read and write) | 可讀可寫 |
b (binary) | 以二進制方式打開文件 |
t (text) | 以文本方式打開文件(默認以該模式打開文件) |
2.常見的組合方式
composed mode | description |
---|---|
r+ | 以讀寫的方式操作文件,允許讀寫.文件必須存在,否則返回NULL.打開成功返回指向文件的指針,指向文件的頭部 (注意很多書上或資料上講述追加方式打開成功后位置指針指向文件末尾是錯誤的); |
rb+ | 以可讀可寫,二進制方式打開文件,允許讀寫.文件必須存在,否則返回NULL.若打開文件成功返回文件指針,指向文件頭部; |
rt+ | 以可讀可寫,文本方式打開文件,允許讀寫.文件必須存在,否則返回NULL.若打開文件成功,返回文件指針,指向文件頭部; |
w | 以只寫的方式打開文件,只允許寫,若文件存在,文件中原有內容會被清除;若文件不存在,則創建文件,打開成功后返回文件指針,位置指針指向文件頭部 |
w+ | 以讀寫的方式打開文件,允許讀寫,若文件存在,文件中原有內容會被清除;若文件不存在,則創建文件,打開成功后返回文件指針,位置指針指向文件頭部 |
a | 以追加只寫的方式打開文件,只允許寫.若文件存在,則追加的內容在文件的末尾,若文件不存在則創建文件.打開成功后返回文件的指針,指向文件的頭部. |
a+ | 以追加、可讀寫的方式打開文件,允許讀寫。若進行讀操作,則從頭開始讀;若進行寫操作,則將內容添加在末尾。若文件不存在,則創建文件。打開成功后返回文件指針,位置指針指向文件頭部(不保留EOF) |
二進制和文本打開方式基本相同,不同的地方是讀取文本是碰到ASCII碼為26的字符是,則會停止文件的讀寫,默認文件以及結束.應為正常的文本不會有ASCII碼26的字符.而二進制打開方式不存在這個問題.(UNIX下沒有區別);
注意:
1)在以追加方式打開文件時,位置指針指向文件的首部。
? 在這里區分一下位置指針和文件指針:
? 文件指針:指向存儲文件信息的一個結構體的指針
? 位置指針:對文件進行讀寫操作時移動的指針
? 在頭文件<stdio.h>中存在一個結構體_iobuf,在VC6.0中選中FILE,然后F12,則可以看到_iobuf的具體定義(UNIX 的大致相同):
struct _iobuf
{
char *_ptr; // 指向buffer中第一個未讀的字節
int _cnt; // 記錄剩余未讀字節的個數
char *_base; // 指向一個字符數組,即這個文件的緩沖
int _flag; // FILE結構所代表的打開文件的一些屬性
int _file; // 用于獲取文件描述,可以使用fileno函數獲得此文件的句柄。
int _charbuf; // 單字節的緩沖,即緩沖大小僅為1個字節,如果為單字節緩沖,_base將無效
int _bufsiz; // 記錄這個緩沖的大小
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定義了一個文件指針,并成功打開一個文件之后,fp只是指向該結構體,而在對文件進行讀寫操作時,fp的值并不會改變,改變的是結構體中_ptr的值,這個_ptr就是位置指針。
? 2)以追加方式打開時,若進行寫操作,則rewind函數和fseek函數不會起到作用,因為以追加方式打開時進行寫操作的話,系統會自動將位置指針移動到末尾。
? 3)當文件打開用于更新時,可以通過文件指針對文件進行讀寫操作,但是如果沒有給出fseek或者rewind的話,讀操作后面不能直接跟寫操作,否則會是無效的寫操作(位置指針會移動,但是需要寫入文件的內容不會被寫入到文件當中),但是寫操作后可以直接跟讀操作。
每次調用fopen()打開文件后都要記得調用fclose()關閉文件
C語言提供了以下幾種文件讀寫方式
1.字符讀寫: fgetc()/fputc() (讀/寫);
fgetc()函數:
(1)一般調用形式: char ch = fgetc(fd);
(2)作用: 文件中讀取一個字符;
(3)返回值:
? 成功:返回值所得到的字符;
? 失敗:返回EOF(-1)。
*注意問的打開方式
fputc()函數:
(1)一般調用形式: char res = fputc(fd,ch);
(2)作用: 文件中寫入一個字符;
(3)返回值
? 成功:函數輸入的字符;
? 失敗:返回EOF(-1)。
說明:函數putchar()是在stdio.h中用預處理命令定義的宏,即:
//define putchar(c) fputc(c,stdout)
char a = 'a';
char ch = fgetc(fd);
char res = fputc(a, fd);
字符串讀寫: fgets()/fputs() (讀/寫);
1.fgets()函數:
-
一般調用形式:char * fgets(char *str ,int num ,FILE *fd); (fd 的mode rt)
參數說明: str 保存從文件中讀取的字符串;
? num 從文件中讀取的字符串中字符個數不超過num-1.在讀入最后一個字符后加上串結束標志'\0'; -
返回值:
成功: 返的字符串;失敗: 返回NULL
?
2.fputs() 文件中寫入字符串
-
一般調用形式: int num = fputs(char *s,FILE *fd); (fd 的mode at+)
s 要寫入的字符串;
fd 待寫入的文件;
返回值:
? 成功: 寫入的字符個數num;
? 失敗: EOF(-1);
int res = fputs("test", fd);
char rs[11];
char *r = fgets(rs, 11, fd);
數據塊讀寫: fwrite()/fread() (讀/寫)
-
一般調用形式:
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);
-
參數說明:
buffer: 緩沖區指針.對fread,它是暫存讀入數據的指針;對fwrite,它是要輸出數據的指針;
size: 要讀寫的字節數;
count: 要進行讀寫多少個size字節的數據項;
fd: 待讀寫的文件指針;
#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;
}
本例程序定義了一個結構stu,說明了兩個結構數組boya和boyb以及兩個結構指針變量pp和qq。pp指向boya,qq指向boyb。程序第14行以讀寫方式打開二進制文件“stu_list”,輸入二個學生數據之后,寫入該文件中,然后把文件內部位置指針移到文件首,讀出兩塊學生數據后,在屏幕上顯示。
格式化讀寫: fscanf()/fprintf() (寫/讀);
一般調用形式:
? (1)int fprintf(FILE *stream,const char *format,[argument]…) 輸出格式化字符串或者將格式化字符串輸出到流 (文件);
? (2)int fscanf(FILE *stream, const char *) 輸入文件中的內容到某個變量中.
? fscanf(fd,"%s",res)
返回值:
? 成功:
? fprintf讀取的字符個數;
? fscanf: 返回1
? 失敗:
? EOF;
int res = fprintf(fd, "%s",s);
if (res == EOF) {
printf("輸入失敗\n");
}
char tmp[26];
//
int len = fscanf(fd, "%s\n",tmp);
?
其他文件相關操作
ftell()函數: 得到流式文件的當前讀寫位置,返回流式文件當前讀寫位置距離文件頭部的字節數.
long ftell(FILE *);
fseek(): 把fd 的文件讀寫位置指針移動到知道的位置;
@param FILE* 待操作的文件指針
@param long 距離起始點的位置
@parma int 計算的起始點
@return 0 success 文件位置指針指向正確的offset,錯誤返回-1
/*
有三類起始點
SEEK_SET 0 文件開頭
SEEK_CUR 1 文件當前位置
SEEK_END 2 文件末尾
*/
int fseek(FILE *, long offset, int origin);
rewind(): 將文件位置指針重新指向一個文件流的開頭
void rewind(FILE *);
eg:
long fileLength = 0;
fseek(fd, 0, SEEK_END);
fileLength = ftell(fd);
rewind(fd);
fflush():清空緩存
所謂flush一個緩沖,是指對寫緩沖而言,將緩沖內的數據全部寫入實際的文件,并將緩沖清空,這樣可以保證文件處于最新的狀態。之所以需要flush,是因為寫緩沖使得文件處于一種不同步的狀態,邏輯上一些數據已經寫入了文件,但實際上這些數據仍然在緩沖中,如果此時程序意外地退出(發生異常或斷電等),那么緩沖里的數據將沒有機會寫入文件。flush可以在一定程度上避免這樣的情況發生。
在這個表中我們還能看到C語言支持兩種緩沖,即行緩沖(Line Buffer)和全緩沖(Full Buffer)。全緩沖是經典的緩沖形式,除了用戶手動調用fflush外,僅當緩沖滿的時候,緩沖才會被自動flush掉。而行緩沖則比較特殊,這種緩沖僅用于文本文件,在輸入輸出遇到一個換行符時,緩沖就會被自動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 通信和文件操作的關系
待續....
C語言文件操作和操作系統文件子系統的關系
參考:
? 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