1.c語言文件操作
fopen
:打開文件,模式?"w"
(寫,覆蓋)或?"r"
(讀)。
fwrite
:fwrite(data, size, count, fp)
,按?size
?字節寫入?count
?次數據。
fread
:fread(buf, size, count, fp)
,返回實際讀取字節數,需結合?feof
?處理文件結束。
fclose
:關閉文件,釋放資源,必須調用以避免泄漏。
1.打開關閉文件:
#include <stdio.h>int main() {FILE *fp = fopen("myfile", "w");if (!fp) {printf("fopen error!\n");}while (1); // 死循環,fclose無法執行fclose(fp);return 0;
}
2.寫文件:
#include <stdio.h>
#include <string.h>int main() {FILE *fp = fopen("myfile", "w");if (!fp) {printf("fopen error!\n");return 1;}const char *msg = "hello bit!\n";for (int i = 0; i < 5; ++i) { // 更清晰的循環fwrite(msg, strlen(msg), 1, fp);}fclose(fp);return 0;
}
3.讀文件:
#include <stdio.h>
#include <string.h>int main() {FILE *fp = fopen("myfile", "r");if (!fp) {printf("fopen error!\n");return 1;}char buf[1024];const char *msg = "hello bit!\n"; // 正確定義指針(原代碼可能筆誤,此處修正)size_t read_len = strlen(msg); // 12字節while (1) {ssize_t s = fread(buf, 1, read_len, fp); // 每次讀取12字節if (s > 0) {buf[s] = '\0'; // 終止字符串printf("%s", buf);}if (feof(fp)) { // 檢查文件結束break;}}fclose(fp);return 0;
}
更加細節的操作,可以再C語言文件操作-CSDN博客文章中查看
stdin &stdout& stderr
C默認會打開三個輸入輸出流,分別是stdin,stdout,stderr仔細觀察發現,
這三個流的類型都是FILE*,fopen返回值類型,文件指針
2.系統文件I/O
認識一下兩個概念:系統調用和庫函數
上面的 fopen fclose freadfwrite 都是C標準庫當中的函數,我們稱之為庫函數(libc)。
而 open close read write lseek 都屬于系統提供的接口,稱之為系統調用接口
?
不管是c c++ 還是java 所有的語言對文件的操作的庫函數,其實都是系統IO套了一層語言的外殼
2.1.系統文件的接口
1.打開文件open:
打開或創建文件,返回文件描述符。
有兩種創建方式:
第一種:就是文件已經存在的情況
第二種:就是文件如果不存在,那么就其文件進行創建,mode參數就是設置創建文件的權限
1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <fcntl.h>4 int open(const char *pathname, int flags);
5 int open(const char *pathname, int flags, mode_t mode);6
7 pathname: 要打開或創建的目標文件
8 flags: 打開文件時,可通過“或”運算組合多個常量,規則如下:
9 參數:
10 基本模式(必選其一,互斥):
11 O_RDONLY:只讀(0)
12 O_WRONLY:只寫(1)
13 O_RDWR :讀寫(2)
14 三者必選且僅選其一
15 附加標志(可選,| 組合):
16 O_CREAT :創建文件(需配mode,不存在則建,存在則按基本模式打開)
17 O_APPEND:追加寫(指針移到末尾,不覆蓋原有內容)
18 O_TRUNC :寫模式下截斷文件(清空內容,覆蓋寫入,與O_APPEND互斥)
19 O_EXCL :與O_CREAT聯用,確保文件不存在(存在則失敗,errno=EEXIST,原子創建)
20 O_NONBLOCK:非阻塞IO(設備/套接字操作不阻塞,立即返回,需處理EAGAIN)
21 O_SYNC :同步寫(數據+元數據立即刷盤,保證持久,性能開銷大)
22 O_DIRECT :直接IO(繞過內核緩沖,需內存/偏移對齊,否則EINVAL)
23 mode: 僅O_CREAT時有效,指定新文件權限(如0644),實際權限為mode & ~umask24 返回值:
25 成功: 非負文件描述符(如3,4...,0/1/2為標準IO)
26 失敗: -1(檢查errno,用perror調試,如ENOENT(文件不存在)、EEXIST(O_CREAT+O_EXCL沖突)等)
2.寫文件write:
向文件寫入緩沖區數據。
3.讀文件read:
關閉文件描述符,釋放資源。
上面三種接口都使到標志位,那么下面就是識別標志位的一種方式:
上面參數flags傳遞標志位的方法:
采用的的位圖的思想,只需要判斷對應的二進制位上是否是1即可
2.1.文件描述符fd
理解文件描述符之前先了解fd代表什么,又是存儲在哪里?
一個進程都有PCB,在PCB中,有個指針數組*files 表示一個進程可以管理多個文件,而指針數組*files 的下標就是我們說的fd ,files[fd]:就是指向下標為fd的文件,系統會自動的的打開三個文件,分別是 標準輸入,標準輸出,標準錯誤,他們對應的下標是 0,1,2
2.1.1.文件描述符的分配規則
2.此時將0和1下標的文件關閉,發現打印的0;
2.1.2.重定向
明白了上面的規則后,看一個現象:
當我們將標準輸出給關閉,printf向輸出文件寫東西的時候,發現內容沒有寫到標準輸出文件,而寫到了新創建的文件
原理:
使用dup2 系統調用:
函數原型:
再看一個示例:發現此時的fd 和 1指向的都是新創建的ll 文件
3.一切皆文件
首先,在windows中是文件的東西,它們在linux中也是文件;其次一些在windows中不是文件的東西,比如進程、磁盤、顯示器、鍵盤這樣硬件設備也被抽象成了文件,你可以使用訪問文件的方法訪問它們獲得信息;甚至管道,也是文件;
這樣做最明顯的好處是,開發者僅需要使用一套 API和開發工具,即可調取 Linux 系統中絕大部分的資源。舉個簡單的例子,Linux中幾乎所有讀(讀文件,讀系統狀態,讀PIPE)的操作都可以用read 函數來進行;幾乎所有更改(更改文件,更改系統參數,寫PIPE)的操作都可以用 write 函數來進行。
4.緩沖區
緩沖區的定義:
緩沖區是內存空間的一部分。也就是說,在內存空間中預留了一定的存儲空間,這些存儲空間用來緩沖輸入或輸出的數據,這部分預留的空間就叫做緩沖區。緩沖區根據其對應的是輸入設備還是輸出設備,分為輸入緩沖區和輸出緩沖區。
在不同層面上也是分成 :
用戶級:語言層緩沖區
語言層的緩沖區內容就緩沖到文件內核緩沖區
系統層: 文件內核緩沖區
文件內核緩沖區內容就緩沖到磁盤中
緩沖區的作用:
讀寫文件時,如果不會開辟對文件操作的緩沖區,直接通過系統調用對磁盤進行操作(讀、寫等),那么每次對文件進行一次讀寫操作時,都需要使用讀寫系統調用來處理此操作,即需要執行一次系統調用,執行一次系統調用將涉及到CPU狀態的切換,即從用戶空間切換到內核空間,實現進程上下文的切換,這將損耗一定的CPU時間,頻繁的磁盤訪問對程序的執行效率造成很大的影響。
為了減少使用系統調用的次數,提高效率,我們就可以采用緩沖機制。比如我們從磁盤里取信息,可以在磁盤文件進行操作時,可以一次從文件中讀出大量的數據到緩沖區中,以后對這部分的訪問就不需要再使用系統調用了,等緩沖區的數據取完后再去磁盤中讀取,這樣就可以減少磁盤的讀寫次數,再加上計算機對緩沖區的操作大大快于對磁盤的操作,故應用緩沖區可大大提高計算機的運行速度。
這時我們的CPU可以處理別的事情。可以看出,緩沖區就是塊內存區,它用在輸入輸出設備和CPU之間,用來緩存數據。它使得低速的輸入輸出設備和高速的CPU能夠協調工作,避免低速的輸入輸出設備占用CPU,解放出CPU,使其能夠高效率工作。
4.1.緩沖類型
緩沖區類型分成下面三種:
?
全緩沖區:
這種緩沖方式要求填滿整個緩沖區后才進行1/0系統調用操作。對于磁盤文件的操作通常使用全緩沖的方式訪問。
原本標準輸出是行緩沖區,但是已經重定向到了“log.txt”文件,所以變成了全緩沖區,所以要使用語言層的刷新函數fflush進行刷新
行緩沖區:
在行緩沖情況下,當在輸入和輸出中遇到換行符時,標準I/0庫函數將會執行系統調用操作。當所操作的流涉及一個終端時(例如標準輸入和標準輸出),使用行緩沖方式。因為標準I/0庫每行的緩沖區長度是固定的,所以只要填滿了緩沖區,即使還沒有遇到換行符,也會執行/O系統調用操作,默認行緩沖區的大小為1024。
原本標準輸出是行緩沖區,但是已經重定向到了“log.txt”文件,所以變成了全緩沖區,所以沒有數據刷新到文件緩沖區
無緩沖區:
無緩沖區是指標準I/0庫不對字符進行緩存,直接調用系統調用。標準出錯流stderr通常是不帶緩沖區的,這使得出錯信息能夠盡快地顯示出來。
標準錯誤就是無緩沖區:
4.2.FILE
觀察此段代碼:
運行結果1:
運行結果2(對進程實現輸出重定向?./hello > file):
重定向結果解析:
1.當重定向的時候,緩沖區的緩沖方式已經變成了從行緩沖變成了全緩沖。
2.所以msg0和msg1寫在了用戶級的緩沖區,msg3調用的是系統IO,所以直接寫在,內核緩沖區中。
3.fork()創建子進程,所以父子進程都結束的時候,對進行二次對用戶級的緩沖區的刷新
4.所以此時打印的是這種結果