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