Linux基礎 ——’文件編程‘是怎么回事?
LINUX基礎 ——文件編程篇
「Linux 一切皆是文件 文件系統(tǒng)(文件夾/文件)硬件設備 ,管道,數(shù)據(jù)庫,Socket等」
一、文件編程概述:
1.應用中比如:「賬單,游戲進度,配置文件等?!?/strong>2.用代碼操作文件:「實現(xiàn)文件創(chuàng)建,打開,編輯等自動化執(zhí)行。」
二、計算機如何幫我們自動化完成以上操作呢?
操作系統(tǒng)提供了一系列的API ?如Linux系統(tǒng):
打開 ? ? ? ? ? ? open
讀寫 ? ? ? ? ? ? write/read
光標定位 ? ? ?lseek
關閉 ? ? ? ? ? ? ?close
三、文件打開及創(chuàng)建
打開/創(chuàng)建文件
頭文件:
「#include
#include #include 」
int open(const char *pathname , int flags); //(參數(shù)1:字符指針 指向文件路徑 , 參數(shù)2:整型數(shù)權限)
int open(const char *pathname , int ?flags , mode_t ?mode); //open 返回值是文件描述符——沒有文件返回值就無法用write(); ?和 ?read();
參數(shù)說明:
*pathname ?: ? //要打開的文件名(含路徑,缺省為當前路徑)
flags ? :
O_RDONLY ? ?只讀打開 O_WRONLY ? 只寫打開 O_RDWR ? ? ? 可讀可寫可打卡
「當我們附帶了權限后,打開的文件就只能按照這種權限來操作?!?/strong>「以上這三個常數(shù)中應當只指定一個?!?/strong>
int?creat(const?char?*filename?,?mode_t?mode)
filename:要創(chuàng)建的文件名(包含路徑,缺省為當前路徑)
mode:創(chuàng)建模式??//可讀可寫可執(zhí)行
常見創(chuàng)建模式:
?宏表示???????數(shù)字
?S_IRUSR???????4??????可讀
?S_IWUSR???????2??????可寫
?S_IXUSR???????1??????可執(zhí)行
?S_IRWXU???????7??????可讀,寫,執(zhí)行?
示例代碼:(在路徑/Home/CLC/下創(chuàng)建了file1可讀可寫可執(zhí)行文件)
#include
#include
#include
#include
#include
int?main()
{
???int?fd;
???char?*buf?=?"test";
???fd?=?creat("/home/CLC/file",S_IRWXU);???//S_IRWXU??可讀可寫可執(zhí)行
???return?0;
}
==「下列常數(shù)是可選擇的:」==
1. O_CREA //若文件不存在則創(chuàng)建它。
使用此選項時,「需要同時說明第三個參數(shù)mode,用其說明該新文件的存取許可權限?!?/strong>
「mode:一定是在flags中使用了O_CREAT標志,mode記錄待創(chuàng)建的文件的訪問權限?!?/strong>
「O_CREAT 若文件不存在則創(chuàng)建它 ? 示例代碼:」
#include
#include
#include
#include
int?main()
{
???int?fd;????//定義整型返回值
???fd?=?open("./file",O_RDWR);??//open指令?打開可讀可寫file文件返回值
???printf("fd?=?%d\n",fd);
???if(fd?==?-1){?????????//若返回值為-1??沒有file文件
??????printf("open?file?failed\n");
??????fd?=?open("./file"?,?O_RDWR?|?O_CREAT?,?0600);???
??????//open?指令?若沒有file文件?”?|?O_CREAT?”?創(chuàng)建file文件?權限0600??6=4+2??所以是rw??可讀可寫
??????if(fd>0){
??????????printf("creat?file?success\n");
??????}???
???}
???printf("fd=%d\n?,fd");
???return?0;
}
結(jié)果:
「注意:」
「fd = open("./file",O_RDWR);」 ? ? //open指令 打開可讀可寫file文件返回值
「fd = open("./file" , O_RDWR ?| O_CREAT , 0600);」
// ?open 指令 若沒有file文件 ” | O_CREAT ” 創(chuàng)建file文件 權限0600 ?6=4+2 ?所以是rw ?可讀可寫
權限0600的含義:
「—表示普通文件 r (4) 可讀 ? ?w(2)可寫 rw 表示可讀可寫 ?x(1)執(zhí)行」「0600 :6=4+2 所以是rw 可讀可寫 0是同組 0其他組」
2. O_EXCL //如果同時指定了OCREAT , 而文件已經(jīng)存在,則打開失敗或者返回-1
「O_EXCL ?//如果同時指定了OCREAT , 而文件已經(jīng)存在,則打開失敗或者返回-1 ? 示例代碼:」
#include
#include
#include
#include
int?main(){
???int?fd;
???fd?=?open("./file",O_RDWR?|?O_CREAT?|?O_EXCL?,?0600);
???if(fd?==?-1){?????//若返回值為-1??沒有file文件
?????printf("file?exist\n");
?????return?0;
???}
}
注意:
「fd = open("./file",O_RDWR | O_CREAT | O_EXCL , 0600);」
3. O_APPEND//每次寫時都加到文件的尾端。
「O_APPEND //每次寫時都加到文件的尾端 ?示例代碼:」
#include
#include
#include
#include
int??main(){
???int?fd;
???char?*buf?=?"19990330";
???
???fd?=?open("./file",O_RDWR?|?O_APPEND);
???
???printf("open?susceess:?fd?=?%d?\n",?fd);
???
???int?n_write?=?write(fd?,buf,strlen(buf));
???
???if(n_write?!=?-1){
??????????printf("write?%d?byte?to?file\n",n_write);
???}
???close(fd);
???return?0;
}
注意:
「fd = open("./file",O_RDWR | O_APPEND);」
4. O_TRUNC屬性去打開文件時,如果這個文件中本來是有內(nèi)容的,而且為只讀或者只寫 成功打開,則將其長度截斷為0。
「O_TRUNC屬性去打開文件時,如果這個文件中本來是有內(nèi)容的,把原先內(nèi)容全部覆蓋掉,示例代碼:」
#include
#include
#include
#include
int?main(){
??int?fd;
??char?*buf?="19990330";
??
??fd?=?open("./file",O_RDWR?|?O_TRUNC);
??printf("open?susceess:fd?=%d\n",fd);
??int?n_write?=?write(fd?,buf?,strlen(buf));
??if(n_write?!=?-1){
????????printf("write?%d?bute?to?file\n",n_write);
??}
??close(fd);
??return?0;
}
注意:
「fd = open("./file",O_RDWR | O_TRUNC);」
ls-l ?是把文件所有內(nèi)容列出來
-rwxr-xr-x 1 CLC book ?8478 ?Fed 9 12:36 ?mode -rw-r --r -- 1 CLC book ? 385 ? ?Fed 9 12:36 ?mode.c -rw ------ 1 CLC ? book ? ? 0 ? ? ?Fed 9 12:44 ?file
—表示普通文件 r (4) 可讀 w(2)可寫 rw 表示可讀可寫 x(1)執(zhí)行
四、文件寫入操作編程
寫入文件:==「write」==
頭文件: ? ?==「#include
「ssize_t write」 (int fd , const void *buf , size_t count);
==「//將緩沖區(qū)buf指針指向內(nèi)存數(shù)據(jù),寫conuct的大小 到 fd里?!?/strong>==
(int fd , const void *buf , size_t count) 參數(shù):(文件描述符 ,【無類型指針是一個緩沖區(qū)】,寫入文件的大?。?/p>
文件寫入操作示例代碼:
#include
#include
#include
#include
#include
#include
int?main()
{
????int?fd;
????char?*buf?=?"Refuel.CONG";
????fd?=?open("./file",O_RDWR);
????printf("fd=%d\n",fd);
????if(fd?==?-1){
????????printf("open?file?failed\n");
????????fd?=?open("./file",O_RDWR?|?O_CREAT?,?0600);
????????if(fd>0){
????????????printf("creat?file?success\n");
????????}
????}
????//ssize_t?write(int?fd?,?const?void?*buf?,?size_t?count);
????//將緩沖區(qū)buf指針指向內(nèi)存數(shù)據(jù),寫conut大小到fd。
????write(fd,buf,strlen(buf));
????printf("fd=%d\n",fd);
????close(fd);???//關閉fd文件
????return?0;
}
strlen(); 函數(shù)是用來 真正計算有效字符長度用strlen 頭文件是:
五、文件讀取操作
讀取文件:==「read」==
頭文件: ?==「#include
「ssize_t read」(int fd , const void *buf , size_t count); ==「//從fd個文件讀取count個字節(jié)數(shù)據(jù)放到buf里」==
返回值:若讀取成功,讀多少個字節(jié)返回多少個字節(jié),若讀到尾什么都沒讀到返回0,讀取失敗返回-1
文件讀取示例代碼:
#include
#include
#include
#include
#include
#include
int?main()
{
???int?fd;
???char?*buf?="Refuel.CONG";
???fd=?open("./file",O_RDWR);
???printf("fd=%d\n",fd);
???if(fd?==?-1){
???????printf("open?file?failed\n");
???????fd?=?open("./file",?O_RDWR?|?O_CREAT?|?,0600);
???????if(fd>0){
????????????printf("create?file?success\n");
???????}
???}
int?n_write?=?write(fd,buf,strlen(buf));
if(n_write?!=?-1){
??????printf("write%d?byte?to?file\n",n_write);??//若讀取成功,返回讀到的??ite
}
char?*readBuf;
readBuf?=?(char*)malloc(sizeof(char)*n_write+1);
int?n_read?=?read(fd,reafBuf?,?n_write);
printf("read=%d?,?context:%s\n",n_read,readBuf);
close(fd);
return?0;
}
結(jié)果什么都沒讀取到:是因為光標的原因,光標在寫入數(shù)據(jù)后,光標停在寫完的位置,讀取的時候光會變后面,什么也沒有所以讀取不到。
==「解決辦法:」==
把光標移動到頭。 重新打開文件。
「重新打開文件的方式解決光標的問題:」
#include
#include
#include
#include
#include
#include
int?main()
{
???int?fd;
???char?*buf?="Refuel.CONG";
???fd=?open("./file",O_RDWR);
???printf("fd=%d\n",fd);
???if(fd?==?-1){
???????printf("open?file?failed\n");
???????fd?=?open("./file",?O_RDWR?|?O_CREAT?|?,0600);
???????if(fd>0){
????????????printf("create?file?success\n");
???????}
???}
int?n_write?=?write(fd,buf,strlen(buf));
if(n_write?!=?-1){
??????printf("write%d?byte?to?file\n",n_write);??//若讀取成功,返回讀到的??ite
}
close(fd);????????????????????//寫完數(shù)據(jù)后關閉文件
fd?=?open("./file",O_RDWR);????//重新打開
char?*readBuf;
readBuf?=?(char*)malloc(sizeof(char)*n_write+1);
int?n_read?=?read(fd,reafBuf?,?n_write);
printf("read=%d?,?context:%s\n",n_read,readBuf);
close(fd);
return?0;
}
結(jié)果為:
六、文件光標移動操作
光標移動:==「lseek」== 頭文件:
==「#include 」 ====「#include 」 ==
宏:
==「SEEK_SET」== ? ?「//指向文件的頭」 ==「SEEK_CUR」== ? 「//指向當前光標位置」 ==「SEEK_END」== ? 「//指向文件的尾」
「off_t ?lseek」(int fd , off_t 「offset」 , int 「whence」); 作用:「將文件讀寫指針相對 whence 移動 offset 個字節(jié)」參數(shù)說明:「(文件描述符,偏移值,固定的位置)」
#include
#include
#include
#include
#include
#include
int?main()
{
???int?fd;
???char?*buf?="Refuel.CONG";
???fd=?open("./file",O_RDWR);
???printf("fd=%d\n",fd);
???if(fd?==?-1){
???????printf("open?file?failed\n");
???????fd?=?open("./file",?O_RDWR?|?O_CREAT?|?,0600);
???????if(fd>0){
????????????printf("create?file?success\n");
???????}
???}
int?n_write?=?write(fd,buf,strlen(buf));
if(n_write?!=?-1){
??????printf("write%d?byte?to?file\n"n_write);?
}
char?*readBuf;
readBuf?=?(char?*)malloc(sizeof?(char)*n_write+1);
lseek?(fd,0,SEEK_SET);???//參數(shù):(文件描述,偏移值,固定的位置)
//lseek(fd,-11?,SEEK_CUR);??//所在光標位置往前偏移11個
int?n_read?=?read(fd?,readBuf,n_write);
printf("read=%d,context:%s\n",n_write,readBuf);
close(fd);
return?0;
lseek (fd,0,SEEK_SET); ? //參數(shù):(文件描述,偏移值,固定的位置) lseek(fd,-11,SEEK_CUR); ?//所在光標位置往前偏移11個
七、文件操作原理簡述
1.文件描述符:
對于內(nèi)核而言,所有打開文件都由文件描述符引用。文件描述符是一個非負整數(shù)。當打開一個現(xiàn)存文件或者創(chuàng)建一個新文件時,內(nèi)核向進程返回一個文件描述符。當讀寫一個文件時,用open和creat返回的文件描述符標識該文件,將其作為參數(shù)傳遞給read和write。按照慣例,UNIX shell 使用文件描述符0與進程的標準輸入相結(jié)合,文件描述符1與標準輸出相結(jié)合,文件描述符2與標準錯誤輸出相結(jié)合。STDIN_FILENO 、STDOUT_FILENO、STDERR_FILENO這幾個宏代替了0,1,2這幾個數(shù)。
文件描述符,這個數(shù)字在一個進程中表示一個特定含義,當我們open一個文件時,操作系統(tǒng)在內(nèi)存中構建了一些數(shù)據(jù)結(jié)構來表示這個動態(tài)文件,然后返回給應用程序一個數(shù)字作為文件描述符,這個數(shù)字就和我們內(nèi)存中維護的這個動態(tài)文件的這些數(shù)據(jù)結(jié)構綁定上了,以后我們應用程序如果要操作這個動態(tài)文件,只需要用這個文件描述符區(qū)分。
文件描述符的作用域就是當前進程,除了這個進程文件描述符就沒有意義了,open函數(shù)打開文件,打開成功返回一個文件描述符,打開失敗,返回-1。
2.linux系統(tǒng)默認:
標準描述符:「標準輸入(0) ?標準輸出(1) 標準錯誤(3)」
#include
#include
#include
#include
#include
#include
int?main(){
????int?fd;
????char?readBuf[128];
????
????int?n_read?=?read(0,readBuf,5);?????//?0?標準輸入
????int?n_write?=?write(1,readBuf,strlen(readBuf));?//?1?標準輸出
????printf("\n?end!\n");
????return?0;
}

3.操作文件時候:
「打開/創(chuàng)建文件 ? ——> ? 讀取文件/寫入文件 ?——> ?關閉文件」==
1、在Linux中要操作一個文件,一般是先open打開一個文件,得到文件描述符,然后對文件進行讀寫操作(或其他操作),最后是close關閉文件即可。
2、強調(diào)一點:我們對文件進行操作時,一定要先打開文件,打開成功之后才能操作,如果打開失敗,就不用進行后邊的操作了,「最后讀寫完成后,一定要關閉文件,否則會造成文件損壞?!?/strong>
3、「文件平時是存放在塊設備中的文件系統(tǒng)文件中的,我們把這種文件叫靜態(tài)文件」,當我們?nèi)pen打開一個文件時,linux內(nèi)核做的操作包括:內(nèi)核在進程中建立一個打開文件的數(shù)據(jù)結(jié)構,記錄下我們打開的這個文件;內(nèi)核在內(nèi)存中申請一段內(nèi)存,并且將靜態(tài)文件的內(nèi)容從塊設備中讀取到內(nèi)核中特定地址管理存放(叫動態(tài)文件)。read write 都是對動態(tài)文件進行操作
4、打開文件以后,以后對這個文件的讀寫操作,都是針對內(nèi)存中的這一份動態(tài)文件的,而并不是針對靜態(tài)文件的。當然我們對動態(tài)文件進行讀寫以后,此時內(nèi)存中動態(tài)文件和快設備文件中的靜態(tài)文件就不同步了,當我們close關閉動態(tài)文件名,close內(nèi)部內(nèi)核將內(nèi)存中的動態(tài)文件的內(nèi)容去更新(同步)塊設備中的靜態(tài)文件。
5、為什么這么設計,不直接對塊設備直接操作。「塊」(假設有100字節(jié))設備本身讀寫非常不靈活,是按塊讀寫的,最小只讀100字節(jié),而「內(nèi)存是擠字節(jié)單位操作的可而具可以隨機操作,很靈活」。
「靜態(tài)文件」放入磁盤中的文件是靜態(tài)文件, 如:桌面上的文件.jpg
「動態(tài)文件」open靜態(tài)文件后,會在linux內(nèi)核產(chǎn)生結(jié)構體記錄文件 ? 如:fd ? 信息節(jié)點,buf(內(nèi)容,內(nèi)存)
調(diào)用close時候,會把所有信息緩存到磁盤中
八、文件操作編程小練習—實現(xiàn)cp指令代碼:
==「cp ( src.c ?//源文件 ,dest.c //復制到的目標文件)」==
「實現(xiàn)cp操作:」
1.c語言參數(shù):./a.out ?_ _ ==2.實現(xiàn)思路:==
打開src.c 讀src到buf 創(chuàng)建/打開dest.c 將buf寫入dest.c close兩個文件
==c語言參數(shù):==「// 參數(shù)(argc是數(shù)組)(argv是二級指針是包含數(shù)組的數(shù)組:是argv里的每一項都是一個數(shù)組)」
#include
int?main(?int??argc?,?char?**agrv)????//?參數(shù)(argc是數(shù)組)(argv是包含數(shù)組的數(shù)組:是argv里的每一項都是一個數(shù)組)
{
????printf("totol?params:%d\n",argc);
????printf("No.1??params:%d\n",argv[0]);
????printf("No.2??params:%d\n",argv[1]);
????printf("No.3??params:%d\n",argv[2]);?
???return?0;
}
輸入 :./a.out ? des ?src 結(jié)果:totol params : 3 No.1 ?params : ?./a.out No.2 ?params : ?des No.3 ?params : src
「實現(xiàn)mycp操作代碼:」
#include
#include
#include
#include
#include
#include
int?main(int?argc?,char?**argv){
????int?fdSrc;
????int?fdDest;
????char?*readBuf?=?NULL;
????if(argc?!=?3){
????????printf("param?error\n");
????????exit(-1);
????}
????
fdSrc?=?open(argv[1],O_RDWR);??????//1.?打開fdSrc.c(源文件)
int?size?=?lseek(fdSrc?,0,?SEEK_END);???//光標記錄文件大小
lseek(fdSrc?,?0,?SEEK_SET);???//??!千萬記得把?光標回到頭
readBuf?=?(char?*)malloc(sizeof(char)?*size+8);??//開辟readBuf空間大小
int?n_read?=?read(fdSrc,readBuf,size);???//2.?讀取fdSrc(源文件)到readBuf緩沖區(qū)
fdDest?=?open(argv[2],O_RDWR?|?O_CREAT?|?O_TRUNC?,0600);???//3.?打開/創(chuàng)建fdDest.c
int?n_write?=?write(fdDest,readBuf,strlen(readBuf));??//4.?將readbuf寫入到fdDest.c
close(fdSrc);????//5.關閉兩個文件
close(fdDest);
return?0;
}
mycp容易出現(xiàn)的小問題—實現(xiàn)優(yōu)化:
「用 lseek來光標計算size數(shù)組」
char?*readBuf?=?NULL;
?int?size?=?lseek(fdSrc,0,SEEK_END);
?????lseek(fdSrc?,?0?,SEEK_SET);
?????readBuf?=?(char?*)malloc(sizeof(char)*size?+?8);
「若要是拷貝大于1024的文件就不行」
?int?n_read?=?read(fdSrc,readBuf,size);????//讀取大小用lseek查出的大小.
3.「目標文件存在并且存在一些數(shù)據(jù),拷貝就會覆蓋了原來數(shù)據(jù)的一部分」
解決方法:「O_TRUNC」 屬性去打開文件時,如果這個文件中本來是有內(nèi)容的,而且為只讀或者只寫成功打開,則將其長度截斷為0
fdDes?=?open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
九、文件編程小應用之修改程序的配置文件
==「(工作中常用)」== 配置文件的修改
例如:SPEED=5 LENG=100 SCORE=90 LEVEL=95
「修改指定內(nèi)容的思路:」
找到(要修改的)位置 (修改的位置)往后移動到(要改的值 )
3. 修改要改的值
找尋修改位置時候,用到 「strstr()」 函數(shù);功能:用來檢索子串在字符串中首次出現(xiàn)的位置
「修改LENG的值 示例代碼:」
#include
#include
#include
#include
#include
#include
#include
int?main(int?argc?,?char?**argv){
???int?fdSrc;
???char?*readBuf=NULL;
???if(argc?!=?2?){
???????printf("pararm?error\n");
???????exit(-1);
???}
??fdSrc?=?open(argv[1],O_RDWR);
??int?size?=?lseek(fdSrc,0,SEEK_END);
??lseek?(fdSrc,0,SEEK_SET);
??
??readBuf?=(char?*)malloc(sizeof(char)*size+8);
??int?n_read=?read(fdSrc,readBuf,size);
??
??char?*p=strstr(readBuf,"LENG=");?//找到(要修改的)位置???
??//參數(shù)1?要找的源文件??2.“要找的字符串”
??if(p==NULL){
????printf("not?found\n");
????exit(-1);
??}
??p=p+strlen("LENG=");??//移動字符串個字節(jié)
??*p='0';??????//*p??取內(nèi)容
??
lseek?(fdSrc,0,SEEK_SET);
int?n_write?=write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
return?0;
}
「可以把封裝成一個函數(shù):」
void?*changefile(int?fd,char?*readbuf,char*?f,char?t){
????????char?*p?=?strstr(readbuf,f);
????????if(p?==?NULL){
???????????printf("no?found\n");
???????????exit(-1);
????????}
????????p?=?p?+?strlen(f);
????????*p?=?t;
?????}
int?main(int?argc,char?**argv)
{
??????changefile(fdSrc,readBuf,"LENG=",'6');
}
十、寫一個整數(shù)到文件
「ssize_t ?write」(int fd , const void *buf , size_t count);
//將緩沖區(qū)buf指針指向內(nèi)存數(shù)據(jù),寫conut大小到fd.
「ssize_t ?read」(int fd , const void *buf , size_t count);
//從fd個文件讀取count個字節(jié)數(shù)據(jù)放到buf里
1.寫入整型數(shù)代碼:
#include
#include
#include
#include
#include
#include
#include
int?main(){
???int?fd;
???int?data?=?100;
???int?data2?=?0;
???fd=open("./flie1",O_RDWR);
???
???int?n_write?=write(fd,?&data,?sizeof(int));
???lseek(fd,0,SEEK_SET);
???int?n_read?=read(fd,?&data2,?sizeof(int));
???printf("read?%d\n",data2);
???close(fd);
???return?0;
}

2.寫入結(jié)構體代碼(1):
struct?Test
{
???int?a;
???char?c;
};
int?main(){
???int?fd;
???int?Test?data?=?{100,'a'};
???int?Test?data2?;
???fd=open("./flie1",O_RDWR);
???
???int?n_write?=write(fd,?&data,?sizeof(struct?Test));
???lseek(fd,0,SEEK_SET);
???int?n_read?=read(fd,?&data2,?sizeof(struct?Test));
???printf("read?%d?%s\n",data2.a,data2.c);
???close(fd);
???return?0;
}
3.寫入結(jié)構體代碼(2):
struct?Test
{
???int?a;
???char?c;
};
int?main(){
???int?fd;
???int?Test?data[2]?=?{{100,'a'},{101,'b'}};
???int?Test?data2[2];
???fd=open("./flie1",O_RDWR);
???
???int?n_write?=write(fd,?&data,?sizeof(struct?Test)*2);
???lseek(fd,0,SEEK_SET);
???int?n_read?=read(fd,?&data2,?sizeof(struct?Test)*2);
???printf("read?%d?%s\n",data2[0].a,data2[0].c);
???printf("read?%d?%s\n",data2[1].a,data2[1].c);
???close(fd);
???return?0;
}
「注意寫入/讀入的 大小:」 ?sizeof(struct Test) 「乘2」
「緩沖區(qū)可以寫入 :整數(shù),字符,結(jié)構體等」
十一、標準C庫對文件操作引入
1. open 和 fopen 的區(qū)別
「1.來源」
open是UNIX系統(tǒng)調(diào)用函數(shù),返回的是文件描述符,它是文件在文件描述符表里的索引。
fopen是ANSIC標準中的c語言庫函數(shù),在不同的系統(tǒng)中應該調(diào)用不同的內(nèi)核API,返回值是一個指向文件結(jié)構的指針。
「2.移植性」
這一點從上面的來源就可以推斷出來,fopen是C標準函數(shù),因此用有良好的移植性;而oprn是UNIX系統(tǒng)調(diào)用,移植性有限。如windows下相似的功能使用API函數(shù)。
「3.適用范圍」
open返回文件描述符,而文件描述符是UNIX系統(tǒng)下的一個重要概念,UNIX下的一切設備都是文件的形式操作,如網(wǎng)絡套件字,硬件設備(驅(qū)動)等。當然包括操作普通正規(guī)文件。fopen是用來操縱普通正規(guī)文件的。
「4.緩沖」
「緩沖文件系統(tǒng)」
緩沖文件系統(tǒng)的特點是:在內(nèi)存開辟一個“緩沖區(qū)”,為程序中的每一個文件使用;當執(zhí)行讀文件的操作時,從磁盤文件將數(shù)據(jù)先讀入內(nèi)存“緩沖區(qū)”,裝滿后再從內(nèi)存“緩沖區(qū)”依此讀出需要的數(shù)據(jù)。執(zhí)行寫文件的操作時,先將數(shù)據(jù)寫入內(nèi)存“緩沖區(qū)”,待內(nèi)存緩沖區(qū)”裝滿后再寫入文件。由此可以看出,內(nèi)存“緩沖區(qū)”的大小,影響著實際操作外存的次數(shù),內(nèi)存“緩沖區(qū)”越大,則操作外存的次數(shù)就少,執(zhí)行速度就快、效率高。一般來說,文件“緩沖區(qū)”的大小隨機器而定。fopen, fclose, fread, fwrite, fgetc, fgets. fputc, fputs, freopen, fseek. ftell, rewind等。
「非緩沖文件系統(tǒng)」
緩沖文件系統(tǒng)是借助文件結(jié)構體指針來對文件進行管理,通過文件指針來對文件進行訪問,既可以讀寫字符、字符電、格式化數(shù)據(jù),也可以讀寫二進制數(shù)據(jù)。非緩沖文件系統(tǒng)依賴于操作系統(tǒng),通過操作系統(tǒng)的功能對文件進行讀寫,是系統(tǒng)級的輸入輸出,它不設文件結(jié)構體指針,只能讀寫二進制文件,但效率高、速度快,由于ANSI標準不再包括非緩沖文件系統(tǒng),因此建議大家最好不要選擇它.open,close, read, write, getc, getchar, putc, putchar等。
一句話總結(jié)一下,就是open無緩沖,fopen有緩沖。前者與read,write等配合使用,后者與freadfwrite等配合使用
使用fopen函數(shù),由于在用戶態(tài)下就有了緩沖,因此進行文件讀寫操作的時候就減少了用戶態(tài)和內(nèi)核態(tài)的切換(切換到內(nèi)核態(tài)調(diào)用還是需要調(diào)用系統(tǒng)調(diào)用API:read,write);而使用open函數(shù),在文件讀寫時則每次都需要進行內(nèi)核態(tài)和用戶態(tài)的切換;表現(xiàn)為,如果順序訪問文件,fopen系列的函數(shù)要比直接調(diào)用open系列的函數(shù)快;如果隨機訪問文件則相反。這樣一總結(jié)梳理,相信大家對于兩個函數(shù)及系列函數(shù)有了一個更全面清晰的認識,也應該知道在什么場合下使用什么樣的函數(shù)更合適 效率更高。
2. fopen(); fwrite(); fread(); 方式寫入數(shù)據(jù)
==FILE ?*「fopen」 ?(const char *path ,const char *mode);==
參數(shù)說明:path :路徑 ? mode ?:用什么方式打開
返回值:FILE 類型
mode 打開模式:
| 模式指令 | 功能說明 |
|---|---|
| r | 只讀方式打開一個文本文件 |
| rb | 只讀方式打開一個二進制文件 |
| w | 只寫方式打開一個文本文件 |
| wb | 只寫方式打開一個二進制文件 |
| a | 追加方式打開一個文本文件 |
| ab | 追加方式打開一個二進制文件 |
| r+ | 可讀可寫方式打開一個文本文件 |
| rb+ | 可讀可寫方式打開一個二進制文件 |
| w+ | 可讀可寫方式創(chuàng)建一個文本文件 |
| wb+ | 讀可寫方式生成一個二進制文件 |
| a+ | 可讀可寫追加方式打開一個文本文件 |
| ab+ | 可讀可寫方式追加一個二進制文件 |
「寫入:」==size_t ? 「fwrite」 ?(const void *ptr ?, size_t size ?, size_t nmemb ?, FILE *stream);==
ptr ? ? 緩沖區(qū) 等同于(buf)
size ?一個字符大?。╯izeof char) 3. nmemb ?個數(shù)
4. stream (哪個文件) which file
「讀取:」==size_t ?「fread」 ?(const void *ptr ,size_t size ,size_t nmemb ,FILE *stream);==
「光標問題:」==int ?「fseek」 ?(FILE *stream ?, long offset ,int whence);==
「示例代碼:」
#include
#include
int?main()
{
FILE?*fp;
char?*str?=?"Refuel.CONG";
char?readBuf[128]={0};
fp?=?fopen("./CONG.txt","w+");???//可讀可寫方式創(chuàng)建一個文本文件?
fwrite(str?sizeof(char),strlen(str),fp);
//一次性寫一個char?寫str個字節(jié),到fp里
fseek(fp,0,SEEK_SET);
fread(readBuf,sizeof(char),strlen(str),fp);
//從fp里?一次讀一個char?讀str個?讀到readBuf里去
printf("read?data:%s\n",readBuf);
fclose(fp);
return?0;
}


也可改寫:fread (readBuf , ?sizeof(char) , ?* strlen(str) , 1 ?,fp);
//讀*strlen(str)個 ?讀 ?1 次
3. n_read 和 n_write 的返回值
「n_read 和 n_write 的返回值取決于第三個參數(shù)」==
int?n_fwrite?=?fwrite(str?sizeof(char)*strlen(str),1,fp);
int?n_fread?=?fread(str?sizeof(char)*strlen(str),1,fp);
printf("read=%d,write=%d\n",n_read,n_write);
結(jié)果:n_read= 1 n_write =1
4. n_fread 和 n_fwrite 返回值區(qū)別
?int?n_fread?=?fread(str?sizeof(char)*strlen(str),100,fp);
?printf("n_read=%d\n",n_read);
結(jié)果為 :n_read = 1
「但是如果寫100結(jié)果就會不同」
?int?n_fwrite?=?fwrite(str?sizeof(char)*strlen(str),100,fp);
?printf("n_write=%d\n",n_write);
結(jié)果為 :n_write= 100
5. 標準c庫寫入結(jié)構體到文件
「fwrite()寫入結(jié)構體代碼:」
#include
#include
struct?Test
{
????int??a;
????char?c;
};
int?main()
{
FILE?*fp;
struct?Test?data1?={1100,'a'};
struct?Test?data2;
fp?=?fopen("./CONG.txt","w+");???//可讀可寫方式創(chuàng)建一個文本文件?
int?n_fwrite?=?fwrite(&data?,?sizeof(stsuct?Test)?,1,?fp);
fseek(fp,0,SEEK_SET);
int?n_fread?=?fread(&data?,?sizeof(stsuct?Test)?,1,?fp);
printf("read?=%d,%s\n",data2.a,data2.c);
fclose(fp);
return?0;
}
結(jié)果:read = 1100 , a
6. fgetc(); ?fputc(); ?feof() 的使用方法;
==「讀字符函數(shù) :fgetc() 函數(shù)的用法」== 作用:從指定的文件中讀一個字符, 函數(shù)調(diào)用的形式為:char ch//字符變量 = fgetc(fp // 文件指針); 我們可以將讀取到的數(shù)據(jù)給到一個字符變量存儲。
「面對要讀取的數(shù)據(jù)繁多的情況,為了減少程序運行的時間復雜度」,要用到 ==「fgets();」== 「fgets();」 ?功能是從指定的文件讀取一個字符串到字符數(shù)組中。函數(shù)的調(diào)用形式為:fgets(字符數(shù)組名,n,文件指針); 參數(shù):n 為一個正整數(shù)。表示從文件中讀出的字符串不超過n-1個字符,在讀入的最后一個字符后加上字符串結(jié)束標志'0',說通俗易懂點就是讀多少?
printf("%s\n",str);?//循環(huán)讀取所有數(shù)據(jù)?while(fgets(str?,100?,fp)){?
//循環(huán)讀取所有數(shù)據(jù),直到fets讀到’\0‘
????printf("%s\n",str);?}?fclose(fp);?return?0?;?```
==「fputc() 函數(shù)的用法」==
int fputc(int c , FILE *stream); 功能:把 c 寫入 文件stream里
==「feof() 函數(shù)的用法」==
int ?feof (FILE *stream );作用:(「是否到尾巴的位置」):「測試在沒到達文件尾巴返回0,到達尾巴返回 非0」
「feof() 函數(shù)使用代碼:」
#include
#include
int?main(){
??FILE?*fp;
??int?i;
??char?c;
??fp?=?fopen("./test.txt",r);
??//沒到達文件尾巴返回0,到達尾巴返回非0
??while(!feof(fp)){??
??//沒到尾巴時返回0,取反?進入while??當?shù)竭_尾巴返回非0??取反=0?退出while
??????c=fgetc(fp);
??????printf("%c",c);
??}
??fclose(fp);
??return?0;
}
「fputc寫入文件代碼:」
#include
#include
int?main(){
??FILE?*fp;
??int?i;
??char?*str?="Refuel.CONG";
??int?len?=?sizeof(str);
??fp?=?fopen("./test.txt","w+");
??if(fp?==??NULL){
????printf("打開文件出現(xiàn)錯誤\n");
????exit(-1);
??}
??for(i=0;i?????fputc(*str,fp);
?????str++;
??}
??fclose(fp);
??return?0;
}
文件編程就到這里結(jié)束了?。。?/p>
文件編程就到這里結(jié)束了?。。?/p>
原文鏈接 https://blog.csdn.net/weixin_44278698/article/details/123581754?utm_source=app&app_version=5.2.1&code=app_1562916241&uLinkId=usr1mkqgl919blen
推薦閱讀:
最近 Github 上爆火的 Chrome 生產(chǎn)力神器 Omni 是什么鬼?
5T技術資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復「1024」,即可免費獲取!


