linux/unix系統的i/o也就是一般所說的低級i/o——作業系統提供的基本io服務,與os綁定,特定于*nix平台。而标準i/o是ansi c建立的一個标準i/o模型,是一個标準函數包和stdio.h頭檔案中的定義,具有一定的可移植性。兩者一個顯著的不同點在于,标準i/o預設采用了緩沖機制,比如調用fopen函數,不僅打開一個檔案,而且建立了一個緩沖區(讀寫模式下将建立兩個緩沖區),還建立了一個包含檔案和緩沖區相關資料的資料結構。低級i/o一般沒有采用緩沖,需要自己建立緩沖區,不過其實在*nix系統中,都是有使用稱為核心緩沖的技術用于提高效率,讀寫調用是在核心緩沖區和程序緩沖區之間進行的資料複制。
1.fopen與open
标準i/o使用fopen函數打開一個檔案:
file* fp=fopen(const char* path,const char *mod)
其中path是檔案名,mod用于指定檔案打開的模式的字元串,比如"r","w","w+","a"等等,可以加上字母b用以指定以二進制模式打開(對于*nix系統,隻有一種檔案類型,是以沒有差別),如果成功打開,傳回一個file檔案指針,如果失敗傳回null,這裡的檔案指針并不是指向實際的檔案,而是一個關于檔案資訊的資料包,其中包括檔案使用的緩沖區資訊。
*nix系統使用open函數用于打開一個檔案:
int fd=open(char *name,int how);
與fopen類似,name表示檔案名字元串,而how指定打開的模式:o_rdonly(隻讀),o_wronly(隻寫),o_rdwr (可讀可寫),還有其他模式請man 2 open。成功傳回一個正整數稱為檔案描述符,這與标準i/o顯著不同,失敗的話傳回-1,與标準i/o傳回null也是不同的。
2.fclose與close
與打開檔案相對的,标準i/o使用fclose關閉檔案,将檔案指針傳入即可,如果成功關閉,傳回0,否則傳回eof
比如:
if(fclose(fp)!=0)
printf("error in closing file");
而*nix使用close用于關閉open打開的檔案,與fclose類似,隻不過當錯誤發生時傳回的是-1,而不是eof,成功關閉同樣是傳回0。c語言用error code來進行錯誤處理的傳統做法。
3.讀檔案,getc,fscanf,fgets和read
标準i/o中進行檔案讀取可以使用getc,一個字元一個字元的讀取,也可以使用gets(讀取标準io讀入的)、fgets以字元串機關進行讀取(讀到遇到的第一個換行字元的後面),gets(接受一個參數,檔案指針)不判斷目标數組是否能夠容納讀入的字元,可能導緻存儲溢出(不建議使用),而fgets使用三個參數:
char * fgets(char *s, int size, file *stream);
第一個參數和gets一樣,用于存儲輸入的位址,第二個參數為整數,表示輸入字元串的最大長度,最後一個參數就是檔案指針,指向要讀取的檔案。最後是fscanf,與scanf類似,隻不過增加了一個參數用于指定操作的檔案,比如fscanf(fp,"%s",words)
*nix系統中使用read函數用于讀取open函數打開的檔案,函數原型如下:
ssize_t numread=read(int fd,void *buf,size_t qty);
其中fd就是open傳回的檔案描述符,buf用于存儲資料的目的緩沖區,而qty指定要讀取的位元組數。如果成功讀取,就傳回讀取的位元組數目(小于等于qty)。
4.判斷檔案結尾,如果嘗試讀取達到檔案結尾,标準io的getc會傳回特殊值eof,而fgets碰到eof會傳回null,而對于*nix的read函數,情況有所不同。read讀取qty指定的位元組數,最終讀取的資料可能沒有你所要求的那麼多(qty),而當讀到結尾再要讀的話,read函數将傳回0.
5.寫檔案:putc,fputs,fprintf和write
與讀檔案相對應的,标準c語言i/o使用putc寫入字元,比如:
putc(ch,fp);
第一個參數是字元,第二個是檔案指針。而fputs與此類似:
fputs(buf,fp);
僅僅是第一個參數換成了字元串位址。而fprintf與printf類似,增加了一個參數用于指定寫入的檔案,比如:
fprintf(stdout,"hello %s.\n","dennis");
切記fscanf和fprintf将file指針作為第一個參數,而putc,fputs則是作為第二個參數。
在*nix系統中提供write函數用于寫入檔案,原型與read類似:
ssize_t result=write(int fd,void *buf ,size_t amt);
fd是檔案描述符,buf是将要寫入的記憶體資料,amt是要寫的位元組數。如果寫入成功傳回寫入的位元組數,通過result與amt的比較可以判斷是否寫入正常,如果寫入失敗傳回-1。write函數僅僅是将資料寫入了緩沖區,何時寫入磁盤由核心決定,如果要強制寫入硬碟,那麼在open的時候選擇o_sync選項,或者調用fsync函數
6.随機存取:fseek()、ftell()和lseek()
标準i/o使用fseek和ftell用于檔案的随機存取,先看看fseek函數原型
int fseek(file *stream, long offset, int whence);
第一個參數是檔案指針,第二個參數是一個long類型的偏移量(offset),表示從起始點開始移動的距離。第三個參數就是用于指定起始點的模式,stdio.h指定了下列模式常量:
seek_set 檔案開始處
seek_cur 目前位置
seek_end 檔案結尾處
看幾個調用例子:
fseek(fp,0l,seek_set); //找到檔案的開始處
fseek(fp,0l,seek_end); //定位到檔案結尾處
fseek(fp,2l,seek_cur); //檔案目前位置向前移動2個位元組數
而ftell函數用于傳回檔案的目前位置,傳回類型是一個long類型,比如下面的調用:
fseek(fp,0l,seek_end);//定位到結尾
long last=ftell(fp); //傳回目前位置
那麼此時的last就是檔案指針fp指向的檔案的位元組數。
與标準i/o類似,*nix系統提供了lseek來完成fseek的功能,原型如下:
off_t lseek(int fildes, off_t offset, int whence);
fildes是檔案描述符,而offset也是偏移量,whence同樣是指定起始點模式,唯一的不同是lseek有傳回值,如果成功就傳回指針變化前的位置,否則傳回-1。是以可以通過下列方法模拟ftell函數來傳回目前偏移量:
off_t currpos;
currpos = lseek(fd, 0, seek_cur);
whence的取值與fseek相同:seek_set,seek_cur,seek_end,但也可以用整數0,1,2相應代替。
最後,以一個例子結尾,通過c語言編寫linux系統的cp指令,先看看使用标準i/o版本的:
#include<stdio.h>
#include<stdlib.h>
void oops(char *,char *);
int main(int ac,char *av[])
{
file *in,*out;
int ch;
if(ac!=3){
fprintf(stderr,"useage:%s source-file target-file.\n",av[0]);
exit(1);
}
if((in=fopen(av[1],"r"))==null)
oops("can not open ",av[1]);
if((out=fopen(av[2],"w"))==null)
oops("can not open ",av[2]);
while((ch=getc(in))!=eof)
putc(ch,out);
if(fclose(in)!=0||fclose(out)!=0)
oops("can not close files.\n"," ");
return 0;
}
void oops(char *s1,char* s2)
fprintf(stderr,"error:%s %s\n",s1,s2);
exit(1);
再看一個使用unix io的版本:
#include<unistd.h>
#include<fcntl.h>
#define buffersize 4096
#define copymode 0644
int in_fd,out_fd,n_chars;
char buf[buffersize];
fprintf(stderr,"useage:%s source-file target-file.\n",av[0]);
exit(1);
if((in_fd=open(av[1],o_rdonly))==-1)
oops("can't open ",av[1]);
if((out_fd=creat(av[2],copymode))==-1)
oops("can't open ",av[2]);
while((n_chars=read(in_fd,buf,buffersize))>0)
if(write(out_fd,buf,n_chars)!=n_chars)
oops("write error to ",av[2]);
if(n_chars==-1)
oops("read error from ",av[1]);
if(close(in_fd)==-1||close(out_fd)==-1)
oops("error closing files","");
void oops(char *s1,char *s2)
fprintf(stderr,"error:%s",s1);
perror(s2);
顯然,在使用unix i/o的時候,你要更多地關注緩沖問題以提高效率,而stdio則不需要考慮。
文章轉自莊周夢蝶 ,原文釋出時間2007-08-22<b></b>