兩種I/O檔案函數
- fopen
ANSI 標準檔案I/O,基於低層次I/O
- open
低層次I/O
ANSI I/O本質
- DOS/WINDOWS平台,MS DOS 檔案讀寫, 組合語言編寫
- LINUX平台,unix/linux檔案讀寫,C語言編寫
檔案類型FILE
包含一個指標
包含一個stream (C語言把檔案看成stream
)
file open
r
Open text file for reading
r+
Open for reading and writing
w Truncate
file to zero length or create text file for writing
w+
The file is created if it does not exist,
otherwise it is truncated
a
Open for
appending (writing at end of file).
The stream is positioned at the end of the file.
fopen三種基本模式r,w,a只允許針對文本文
件
C語言允許最大同時開啟16個檔案
實際只允許同時開啟13個檔案,因為有另外三個標準檔案stdout,stdin,stderr
檔案讀寫的基本語句
1 |
#include <stdio.h> FILE *stream; 注意類型是大寫 |
2 |
if((stream=fopen("testtmp","r+"))==NULL) { printf("open file error/n"); return; } |
3 |
int ret; if ((ret=fclose(stream))==-1) printf("close file error/n"); |
順序讀檔案(按字元)
while((c=fgetc(stream))!=
EOF
)
printf("%c",c);
每讀一個字元,檔案指標會(自動)移動一格
順序讀檔案(按字串)-------從一個檔案依次讀一行,直到讀到NULL
char record[100],*re;
while ((re=fgets(record,100
,stream))!=NULL
)
printf("%s",record);
讀一行,以斷行符號換行作為讀一行的尾部標誌
從檔案中fgets的字串是讀一行,即包括了斷行符號
所以printf("%s")即可,不用"%s/n",因為字串裡面帶了斷行符號了
這也是個漏洞,字串裡帶了斷行符號
所以非常不利於字串比對strcmp
scanf讀檔案,檔案指標不移動,需要手動fseek移指標
fseek(stream,3,SEEK_SET); fscanf(stream,"%c",&c[0]); fseek(stream,11,SEEK_SET); fscanf(stream,"%c",&c[1]); |
檔案讀寫到檔案末尾
檔案指標會指到NULL
(而不是EOF,EOF不是地址,而是結尾字元
)
-
fput(),fgetc()會返回EOF
-
fgets()會返回NULL
linux和DOS對檔案的結束判別是不同的
linux沒有檔案結束符
,是以目錄項的檔案長度做為檔案結束的判別手段
DOS看0x1a檔案結束符
linux 和DOS的分行符號的區別
linux |
DOS |
0x0a 即 10
即’/n’ 即LF |
CR,LF雙碼 |
讀檔案時, 不能用unsigned char c作為傳回值,
因為EOF不在unsigned範圍內
char c;
c=getc(stream);
fscanf讀文字檔裡面的數字,既可以直接用%c,也可以用%d
用%c(char)讀,可以讀出3,1 |
用%d(int)讀,也可以讀出3,1 |
fseek(stream,3,SEEK_SET); fscanf(stream,"%c",&c[0]); fseek(stream,11,SEEK_SET); fscanf(stream,"%c",&c[1]); |
fseek(stream,ii,SEEK_SET); fscanf(stream,"%d",&ln[i].frame); fseek(stream,ii+8,SEEK_SET); fscanf(stream,"%d",&ln[i].line); |
只不過這兩個3,1,一個是實際值3,1,一個是'3','1'
幾個常見特殊字元的整型數字
char |
int |
空格' ' |
32 |
TAB鍵 |
9 |
斷行符號 |
10 |
檔案結束EOF |
-1 |
字串結束符號 |
數字0(不是字元‘0’) |
TAB字元處理要小心,經過到記事本copy/paste後,TAB鍵被轉化成幾個空格
for(;str[i]==' '||str[i]==' ';i++); |
但經過到記事本copy/paste後,TAB鍵被轉化成幾個空格 所以系統總報warning: tmp.c:58: warning: comparison is always false due to limited range of data type tmp.c:59:27: warning: character constant too long for its type |
fgets字串指標改成字串數組,消滅了Segmentation
fault錯誤
char *re,*rec; re=fgets(rec,100,srcstream); 出Segmentation fault錯誤 |
改成 char *re,rec[100]; re=fgets(rec,100,srcstream); 錯誤消失 |
一個普通的顯示檔案內容
的函數
printfile(FILE *stream,char *filename,int or) { int i,re; char rec[100]; FILE *tmpstream; if (or) { if((tmpstream=fopen(filename,"r"))==NULL) { printf("open file error/n"); return 0; } for(i=0;(re=fgets(rec,100,tmpstream))!=NULL;i++) printf("[%d]:%s",i,rec); 顯示檔案內容時,每行加行號 if ((re=fclose(tmpstream))==-1) printf("close file error/n"); } else { fseek(stream,0,SEEK_SET); for(i=0;(re=fgets(rec,100,stream))!=NULL;i++) printf("[%d]:%s",i,rec); } } |
printfile(NULL,"testtmp.tmp",1 );[0]:total 48 [1]:1 macg macg 3301 Jan 16 02:16 file.c [2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile [3]:1 macg macg 0 Jan 16 02:56 testtmp [4]:this is a testthis is a testthis is a testthis is a testthis is a testth [5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o |
if((stream=fopen("testtmp","r+"))==NULL) { printf("open file error/n"); return 0; } printfile(stream ,NULL,0 );[0]:total 48 [1]:-rw-rw-r-- 1 macg macg 3301 Jan 16 02:16 file.c [2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile [3]: -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp [4]:this is a testthis is a testthis is a testthis is a testthis is a testth [5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o |
按SED/AWK原理,讀記錄/行的函數——是從檔案中取第recno行
int getrecord(char *rec,FILE *stream,int recno) { int i,ret; char *re; for(i=0;i<=recno;i++) re=fgets(rec,100,stream); 數行,並一邊數一邊讀行if(re==NULL) return 0; 如果所取行超過檔案長度,則返回0 else return 1; } |
getrecord(record,stream,4); 取記錄/行,取檔案第四行 printf("record is %s/n",record); printf("/n/n/n"); |
$ ./tmp record is -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp |
按SED/AWK原理,讀欄位的函數——從字串中取第valueno欄位
int getvalue(char *str,int valueno,char *value) { int i,j,vali;數欄位
for(i=0,vali=0;vali<valueno;vali++) { for(;str[i]==' '||str[i]==9;i++); for(;str[i]!=' '&&str[i]!=9&&str[i]!=0;i++); if(str[i]==0) return 0; } 取欄位
for(;str[i]==' '||str[i]==9;i++); for(j=0;str[i]!=' '&&str[i]!=9&&str[i]!=0;i++,j++) { value[j]=str[i]; } value[j]=0; } |
for(i=0;i<20;i++) { if(getvalue(record,i,val)) printf("no %d is %s/n",i,val); } |
$ ./tmp record is -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp 事先故意編輯過此行,加了幾個TAB,連行尾也含TAB no 0 is -rw-rw-r-- no 1 is 1 no 2 is macg no 3 is macg no 4 is 0 no 5 is Jan no 6 is 16 no 7 is 02:56 no 8 is testtmp no 9 is |
fputs(stren,streamwrite)
寫入的串不帶斷行符號,fprintf(strea,"%s",str)寫入的串也不帶斷行符號,所以,要寫入檔案斷行符號,必須寫入/n
fputs(stren,streamwrite); cat aaa.txt 008421aa
|
fprintf(streamwrite,"%s/n ",stren);cat aaa.txt 008421
aa
|
檔案很難進行“修改寫”,建議檔案追加
最好的辦法就是寫到新檔案
,或寫到stdout再重新導向入檔案。然後把新檔案覆蓋舊檔案
writefile(char *tmpfilename,char
*srcfilename,struct Filerecord *frs,int
frsi)-------修改檔案的函數
實際是從 srcfile讀出檔案
把其中符合frs[j].recno的行,修改成frs[j].record
其他行不變,仍舊用srcfile讀出的行
然後把這些行重新寫入一個新檔案tmpfile
writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi o tmpfilename 新檔案名稱o srcfilename 舊檔案名稱 o struct Filerecord *frs 放置要修改的行的結構體數組 struct Filerecord{ char record[100]; int recno; }; struct Filerecord fr[20]; o frsi 結構體數組的元素數量 |
writefile(char *tmpfilename,char *srcfilename,struct Filerecord *frs,int frsi) { int no,ret,i,j,bol; char *re,rec[100]; FILE *tmpstream,*srcstream;if((tmpstream=fopen(tmpfilename,"w+ "))==NULL) 寫入一個新 { printf("open file error/n"); return 0; } if((srcstream=fopen(srcfilename,"r"))==NULL) { printf("open file error/n"); return 0; } for(no=0;(re=fgets(rec,100,srcstream))!=NULL;no++) { for(j=0,bol=1;j<frsi&&bol;j++) { if(no==frs[j].recno) { bol=0; strcpy(rec,frs[j].record); } } fputs(rec,tmpstream); } if ((ret=fclose(tmpstream))==-1) printf("close file error/n"); if ((ret=fclose(srcstream))==-1) printf("close file error/n"); } |
例子:去掉檔案中行的TAB鍵和起首空格或TAB 執行: 讀出原檔案每行,checkinvaild() ,看是否含有TAB,起首空格/TAB 如果確實有,就對此行處理,重新組合,去掉TAB,起首空格/TAB,然後寫入struct person fr[20]數組 writefile("testtmp.tmp","testtmp",fr,fri); ------------------the sick file---------------------------------------- [0]:total 48 [1]:-rw-rw-r-- 1 macg macg 3301 Jan 16 02:16 file.c [2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile [3]: -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp [4]:this is a testthis is a testthis is a testthis is a testthis is a testth [5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o --------------------modify line-------------------------------------- fri is 2,the new line is following: [1]row:1 macg macg 3301 Jan 16 02:16 file.c [3]row:1 macg macg 0 Jan 16 02:56 testtmp -------------------new file--------------------------------------- [0]:total 48 [1]:1 macg macg 3301 Jan 16 02:16 file.c [2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile [3]:1 macg macg 0 Jan 16 02:56 testtmp [4]:this is a testthis is a testthis is a testthis is a testthis is a testth [5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o |
fseek(stream,offset,mode)的三個mode
SEEK_SET:從開頭數第幾個offset
SEEK_CUR: 從當前數第幾個offset
SEEK_END: 從結尾倒數第幾個offset
檔案指標位置是從0(檔案頭)開始算的
fseek(stream,0,SEEK_SET);
int ftell(FILE
*stream)
返回stream的當前指標位置
簡單的取檔案大小size的操作
fseek(stream,0,SEEK_END);先將指標指向檔案尾部 ret=ftell(stream); 再獲得指標所指的當前地址 這個指向檔案尾部的指標地址就是檔案大小 |
取檔案大小的操作對讀寫檔案很有用,因為讀寫檔案很不好判定檔案結尾。
檔案例子
$ cat tmp.c #define DEBUG 0#include <stdio.h> #include <string.h> #include <unistd.h> struct Filerecord{ char record[100]; int recno; }; main() { char directory[100]; #ifdef SYST getcwd(directory,100); printf("current directory is %s/n",directory); #endif filemanage(); } filemanage() { int i,j,fri,ret; char record[100],val[50],modi_rec[100]; FILE *stream; struct Filerecord fr[20]; if((stream=fopen("testtmp","r+"))==NULL) { printf("open file error/n"); return 0; } printf("------------------the sick file-----------------------/n"); printfile(stream,NULL,0); for(i=0,fri=0;getrecord(record,stream,i);i++) { if (DEBUG) printf("no %d row is:%s/n",i,record); if(ret=findinvalid(record)) { for(j=0;getvalue(record,j,val);j++) { if (DEBUG) printf("no %d's val is %s %d/n",j,val,val[0]); if(j==1) strcpy(modi_rec,val); if(j>1) { strcat(modi_rec," "); strcat(modi_rec,val); } } fr[fri].recno=i; strcpy(fr[fri].record,modi_rec); if (DEBUG) printf("fr[%d] is %d %s/n",fri,fr[fri].recno,fr[fri].record); fri++; } if (DEBUG) printf("---------------------------------/n"); } printf("--------------------modify line--------------------/n"); printf("fri is %d,the new line is following: /n",fri); for(i=0;i<fri;i++) printf("[%d]row:%s",fr[i].recno,fr[i].record); if ((ret=fclose(stream))==-1) { printf("close file error/n"); return 0; } writefile("testtmp.tmp","testtmp",fr,fri); printf("-------------------new file-----------------/n"); printfile(NULL,"testtmp.tmp",1); } int findinvalid(char *str) { int i,ret; i=0; if(str[i]==' ') { if (DEBUG) printf("str[%d] == %d/n",i,str[i]); return 32; } for(i=0;str[i]!=9&&str[i]!=0;i++) ; if(str[i]==9) { if (DEBUG) printf("str[%d] == %d/n",i,str[i]); return 9; } else { if (DEBUG) printf("str[%d] == %d/n",i,str[i]); return 0; } } |
$ make gcc -g -c tmp.c tmp.c: In function 鈥榩rintfile鈥? tmp.c:158: warning: assignment makes integer from pointer without a cast tmp.c:158: warning: comparison between pointer and integer tmp.c:165: warning: assignment makes integer from pointer without a cast tmp.c:165: warning: comparison between pointer and integer gcc -o tmp tmp.o -g |
$ ./tmp ------------------the sick file---------------------- [0]:total 48 [1]:-rw-rw-r-- 1 macg macg 3301 Jan 16 02:16 file.c [2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile [3]: -rw-rw-r-- 1 macg macg 0 Jan 16 02:56 testtmp [4]:this is a testthis is a testthis is a testthis is a testthis is a testth [5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o --------------------modify line-------------------- fri is 2,the new line is following: [1]row:1 macg macg 3301 Jan 16 02:16 file.c [3]row:1 macg macg 0 Jan 16 02:56 testtmp -------------------new file------------------------- [0]:total 48 [1]:1 macg macg 3301 Jan 16 02:16 file.c [2]:-rw-rw-r-- 1 macg macg 52 Jan 16 02:19 Makefile [3]:1 macg macg 0 Jan 16 02:56 testtmp [4]:this is a testthis is a testthis is a testthis is a testthis is a testth [5]:-rw-rw-r-- 1 macg macg 1428 Jan 16 02:56 tmp.o |