文章目錄
一、C語言的文件IO相關(guān)函數(shù)操作
1、1 fopen與fclose
1、2 fwrite
1、3 fprintf與fscanf
1、4?fgets與fputs
二、系統(tǒng)調(diào)用相關(guān)接口
2、1 open與close
2、2 write和read
三、簡(jiǎn)易模擬實(shí)現(xiàn)cat指令
四、總結(jié)
???♂??作者:@Ggggggtm????♂?
???專欄:Linux從入門到精通? ??
???標(biāo)題:文件操作??
????寄語:與其忙著訴苦,不如低頭趕路,奮路前行,終將遇到一番好風(fēng)景????
? 本篇文章主要會(huì)講解C語言的文件IO相關(guān)操作的函數(shù),同時(shí)也會(huì)對(duì)Linux下的文件操作系統(tǒng)調(diào)用接口進(jìn)行講解。希望本篇文章會(huì)對(duì)你有所幫助。
一、C語言的文件IO相關(guān)函數(shù)操作
1、1 fopen與fclose
? fopen() 函數(shù)原型:FILE *fopen(const char *filename, const char *mode); 作用:打開一個(gè)文件,并返回一個(gè)文件指針。 參數(shù):
- filename:要打開的文件名(含路徑)。
- mode:打開文件的模式,如 "r" 表示只讀,"w" 表示寫入(如果文件存在則清空內(nèi)容),"a" 表示追加寫入等。
? 具體如下圖:
? fclose() 函數(shù)原型:int fclose(FILE *stream); 作用:關(guān)閉一個(gè)打開的文件。 參數(shù):
- stream:要關(guān)閉的文件指針。
? ?具體可結(jié)合下圖理解:
? 我們知道使用 fopen 時(shí)需要添加索要打開文件的路徑。當(dāng)我們以w的方式進(jìn)行打開時(shí),該文件不存在會(huì)自動(dòng)創(chuàng)建文件,具體代碼如下圖:
#include<stdio.h> int main() { FILE* fd = fopen("log.txt","w"); if(fd==NULL) { perror("fopen"); } fclose(fd); //while死循環(huán)完全是為了方便查看和觀察 while(1) { sleep(1); } return 0; }
? 上述代碼中,fopen中并沒有添加路徑,只有一個(gè)文件名字。那么能打開成功嗎?其次是,當(dāng)前目錄下并沒有 log.txt 文件,如果能打開成功,文件會(huì)被創(chuàng)建到哪里呢?我們帶著這些疑問接著往下看。
? 我們不妨先觀察一下運(yùn)行結(jié)果,如下圖:
? 我們看到確實(shí)能夠出創(chuàng)建出來,也是創(chuàng)建在了當(dāng)前目錄了!這是為什么呢?當(dāng)一個(gè)程序運(yùn)行起來后,會(huì)在內(nèi)存中創(chuàng)建相應(yīng)的數(shù)據(jù)結(jié)構(gòu),同時(shí)變成進(jìn)程。該進(jìn)程包含了當(dāng)前所在的工作目錄,且還有當(dāng)前的可執(zhí)行文件所在的目錄。具體如下圖:
? 所以即使我們并沒有添加路徑,操作系統(tǒng)也會(huì)知道當(dāng)前所在的路徑。并且默認(rèn)創(chuàng)建到當(dāng)前的工作路徑下。
1、2 fwrite
? fwrite的函數(shù)原型:size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream)。用于將數(shù)據(jù)塊按字節(jié)寫入文件。參數(shù):
- ptr:指向要寫入的數(shù)據(jù)的指針。
- size:要寫入的每個(gè)數(shù)據(jù)項(xiàng)的字節(jié)數(shù)。
- count:要寫入的數(shù)據(jù)項(xiàng)的數(shù)量。
- stream:目標(biāo)文件的指針。
? 我們也可看下圖理解:
? 我們現(xiàn)在用fwrite向log.txt中寫入,具體代碼如下:
#include<stdio.h> #include<string.h> int main() { FILE* fp = fopen("log.txt","w"); if(fp==NULL) { perror("fopen"); } //進(jìn)行文件操作 const char *s1 = "hello Linux\n"; fwrite(s1, strlen(s1), 1, fp); fclose(fp); return 0; }
? ?這里有一個(gè)問題:在寫入文件時(shí),要不要把s1的‘\0’寫入呢?答案是不用。字符串結(jié)尾標(biāo)志'\0'只是C語言規(guī)定的。文件并不用遵守C語言的規(guī)則!我們看運(yùn)行結(jié)果:
? log.txt文件中確實(shí)被寫入了。假如我們注釋掉寫入的代碼,只是打開后直接關(guān)閉文件,結(jié)果會(huì)是什么呢?如下圖:
? 文件內(nèi)容為空了?。?!為什么呢?原因是以“w”的方式打開文件,就是在寫入前會(huì)被清空文件內(nèi)容。?
1、3 fprintf與fscanf
? fprintf() 函數(shù)原型:int fprintf(FILE *stream, const char *format, ...); 作用:向文件中按指定格式寫入數(shù)據(jù)。 參數(shù):
- stream:要寫入的文件指針。
- format:格式化字符串,類似于printf()函數(shù)的格式化參數(shù)。 返回值:成功寫入的字符數(shù),出錯(cuò)時(shí)返回負(fù)值。
? fprintf與printf相比,fprintf第一個(gè)參數(shù)是FILE* ,其他的都一樣。只不過是輸出到了指定的文件上。
? fscanf() 函數(shù)原型:int fscanf(FILE *stream, const char *format, ...); 作用:從文件中按指定格式讀取數(shù)據(jù)。 參數(shù):
- stream:要讀取的文件指針。
- format:格式化字符串,類似于scanf()函數(shù)的格式化參數(shù)。 返回值:成功讀取并匹配的項(xiàng)目數(shù)量,出錯(cuò)或到達(dá)文件結(jié)尾時(shí)返回EOF。
? fscanf() 與scanf() 相比,fscanf() 第一個(gè)參數(shù)是FILE* ,其他的都一樣。讀取數(shù)據(jù)時(shí),是從指定的文件上讀取。
? 這里我們就舉例說明fsacnf,我們想要把文件的內(nèi)容讀取并輸出,代碼如下:
#include<stdio.h> #include<unistd.h> #include<string.h> int main() { FILE* fp = fopen("log.txt","r"); if(fp==NULL) { perror("fopen"); } char line[128]; while(fscanf(fp,"%s",line) != EOF) { printf("%s\n", line); } return 0; }
? 運(yùn)行結(jié)果如下:
? 注意,當(dāng)我們要讀取內(nèi)容是,要修改打開文件的方式,應(yīng)該以“r”的方式打開文件。否則會(huì)出現(xiàn)意想不到的結(jié)果!?。?/strong>
1、4?fgets與fputs
? fgets() 函數(shù)原型:char *fgets(char *str, int n, FILE *stream); 作用:從文件中讀取一行字符串。 參數(shù):
- str:要讀取的字符串存放的緩沖區(qū)。
- n:最多讀取的字符數(shù)(包括換行符)。
- stream:要讀取的文件指針。 返回值:成功時(shí)返回str,失敗或到達(dá)文件結(jié)尾時(shí)返回NULL。
? ?具體如下圖:
? fputs() 函數(shù)原型:int fputs(const char *str, FILE *stream); 作用:向文件中寫入一個(gè)字符串。 參數(shù):
- str:要寫入的字符串。
- stream:要寫入的文件指針。 返回值:成功寫入的字符數(shù),出錯(cuò)時(shí)返回EOF。
?具體如下圖:
? 我們結(jié)合下述實(shí)例來理解fgets和fputs的用法,代碼如下:
#include<stdio.h> #include<unistd.h> #include<string.h> int main() { FILE* fp = fopen("log.txt","r"); if(fp==NULL) { perror("fopen"); } char line[64]; //fgets -> C -> s(string) -> 會(huì)自動(dòng)在字符結(jié)尾添加\0 while(fgets(line, sizeof(line), fp) != NULL) { //printf("%s", line); fputs(line, stdout); //輸出到屏幕上 } fclose(fp); return 0; } ?
? ?運(yùn)行結(jié)果如下:
二、系統(tǒng)調(diào)用相關(guān)接口
2、1 open與close
? ?open()函數(shù):open函數(shù)用于打開文件并獲取文件描述符。它接受一個(gè)文件路徑和一組標(biāo)志作為參數(shù),并返回一個(gè)用于后續(xù)文件操作的文件描述符。函數(shù)原型:int open(const char *pathname, int flags, mode_t mode)。詳細(xì)解釋:
- 函數(shù)說明:打開文件并獲取文件描述符。
- 參數(shù):
- pathname:要打開的文件路徑。
- flags:打開文件的標(biāo)志,例如O_RDONLY(只讀)、O_WRONLY(只寫)、O_RDWR(讀寫)等。
- mode:在創(chuàng)建新文件時(shí)使用的權(quán)限位。
- 返回值:
- 成功:返回一個(gè)非負(fù)整數(shù),表示文件描述符。
- 失?。悍祷?1,并設(shè)置errno來指示錯(cuò)誤。
? 具體可結(jié)合下圖理解:
? 有很多的選項(xiàng),我們想要添加那個(gè)選項(xiàng),只需要在第二個(gè)參數(shù) 按位與(‘|’)?上就行。為什么 按位與(‘|’)呢?這里涉及到了位圖的知識(shí)。想要知道的可以去了解一下位圖。
? close()函數(shù):close函數(shù)用于關(guān)閉打開的文件。它接受文件描述符作為參數(shù),并返回一個(gè)表示成功與否的狀態(tài)值。? ?函數(shù)原型:int close(int fd)。詳細(xì)解釋:
- 函數(shù)說明:關(guān)閉打開的文件。
- 參數(shù):
- fd:要關(guān)閉的文件描述符。
- 返回值:
- 成功:返回0。
- 失?。悍祷?1,并設(shè)置errno來指示錯(cuò)誤。
? 我們通過如下實(shí)例來理解open和close。代碼如下:
#include<stdio.h> #include<unistd.h> #include<string.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> int main() { int fd1 = open("log.txt", O_RDONLY); int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666); //rw-rw-rw- if(fd1 < 0 || fd2 < 0) { perror("open"); return 1; } close(fd1); close(fd2); return 0; }
? 運(yùn)行結(jié)果如下:
? 確實(shí)打開成功了。為什么log2.txt與我們?cè)O(shè)置的權(quán)限并不相同呢?不要忘記了系統(tǒng)中還有umask掩碼。
2、2 write和read
? write()函數(shù):write函數(shù)用于向文件中寫入數(shù)據(jù)。它接受文件描述符、要寫入的數(shù)據(jù)和字節(jié)數(shù)作為參數(shù),并返回實(shí)際寫入的字節(jié)數(shù)。函數(shù)原型:ssize_t write(int fd, const void *buf, size_t count)。詳細(xì)解釋:
- 函數(shù)說明:向文件中寫入數(shù)據(jù)。
- 參數(shù):
- fd:要寫入的文件描述符。
- buf:要寫入的數(shù)據(jù)的緩沖區(qū)。
- count:要寫入的字節(jié)數(shù)。
- 返回值:
- 成功:返回實(shí)際寫入的字節(jié)數(shù)。
- 失?。悍祷?1,并設(shè)置errno來指示錯(cuò)誤。
? read()函數(shù):read函數(shù)從文件中讀取數(shù)據(jù)。它接受文件描述符、緩沖區(qū)指針和要讀取的字節(jié)數(shù)作為參數(shù),并返回實(shí)際讀取的字節(jié)數(shù)。函數(shù)原型:ssize_t read(int fd, void *buf, size_t count)。詳細(xì)解釋:
- 函數(shù)說明:從文件中讀取數(shù)據(jù)。
- 參數(shù):
- fd:要讀取的文件描述符。
- buf:用于存儲(chǔ)讀取數(shù)據(jù)的緩沖區(qū)。
- count:要讀取的最大字節(jié)數(shù)。
- 返回值:
- 成功:返回實(shí)際讀取的字節(jié)數(shù)。
- 失?。悍祷?1,并設(shè)置errno來指示錯(cuò)誤。
? write和read用起來也相對(duì)簡(jiǎn)單。這里就不再舉例詳細(xì)解釋說明了。?
三、簡(jiǎn)易模擬實(shí)現(xiàn)cat指令
? 我們知道,cat是打印出一個(gè)文件的內(nèi)容。我們學(xué)習(xí)了文件操作后,就來簡(jiǎn)單的模擬實(shí)現(xiàn)一下cat指令。
? cat指令不就是接受到文件,然后打印出文件的內(nèi)容嗎。這好像就是我們剛剛學(xué)了文件操作。我們直接看代碼:
#include<stdio.h> #include<unistd.h> #include<string.h> int main(int argc,char* argv[]) { if(argc != 2) { printf("argv error!\n"); return 1; } FILE *fp = fopen(argv[1], "r"); if(fp == NULL) { //strerror perror("fopen"); return 2; } //按行讀取 char line[64]; //fgets -> C -> s(string) -> 會(huì)自動(dòng)在字符結(jié)尾添加\0 // 將文件的內(nèi)容打印到屏幕上 while(fgets(line, sizeof(line), fp) != NULL) { //printf("%s", line); fprintf(stdout, "%s", line); //fprintf->stdout? } fclose(fp); return 0; }
? 在運(yùn)行時(shí)加上文件名就可以打印出文件的內(nèi)容了。再加上我們之前講到的創(chuàng)建子進(jìn)程進(jìn)行程序替換實(shí)現(xiàn)的簡(jiǎn)易版的shell,不就是實(shí)現(xiàn)了cat指令嘛?。?!我們看輸出結(jié)果:
四、總結(jié)
? 本篇文章講述了一系列的文件操作函數(shù)。其實(shí)我們學(xué)的C語言的文件操作函數(shù),底層都是封裝的系統(tǒng)調(diào)用的接口。因?yàn)槲覀儗?duì)文件的寫入和讀取,不就是對(duì)硬盤的寫入和讀取嗎!文件可是放在硬盤上的。語言想要訪問硬件設(shè)備,必須通過操作系統(tǒng)!??!?
? 每套語言都是有自己的文件操作函數(shù),底層都是封裝的系統(tǒng)調(diào)用的接口。但是操作系統(tǒng)不只是有Linux,還有windows等等。那語言就是封裝所有的操作系統(tǒng)的接口唄。只不過是在調(diào)用時(shí)會(huì)有選擇判斷。這樣封裝后,語言就有了跨平臺(tái)性。
? 上文中有一個(gè)名詞:文件描述符。我們并沒有對(duì)此進(jìn)行詳解。下篇文章會(huì)對(duì)此進(jìn)行講解。這個(gè)也是一個(gè)重點(diǎn)!??!文章來源:http://www.zghlxwxcb.cn/news/detail-644898.html
? 本片文章的講解就到這里。感謝閱讀ovo~?文章來源地址http://www.zghlxwxcb.cn/news/detail-644898.html
到了這里,關(guān)于【Linux從入門到精通】文件I/O操作(C語言vs系統(tǒng)調(diào)用)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!