電子商務(wù)自助建網(wǎng)站百度入口提交
🎬?個(gè)人主頁:誰在夜里看海.
📖?個(gè)人專欄:《C++系列》《Linux系列》《算法系列》
???道阻且長,行則將至
目錄
📚前言:一切皆文件?
📚一、C語言的文件接口
📖1.文件打開
🔖語法
🔖本質(zhì)
🔖示例
📖2.文件讀取
🔖語法
🔖示例
📖3.文件寫入
🔖語法
🔖示例
📖4.文件關(guān)閉
🔖語法
🔖作用
📖5.默認(rèn)流指針
📚二、系統(tǒng)調(diào)用接口
📖1.文件打開
🔖語法
🔖示例
📖2.文件讀取
🔖語法
🔖示例
📖3.文件寫入
🔖語法
🔖示例
📖4.文件關(guān)閉
🔖語法
📚三、底層調(diào)用&上層封裝
📖1.底層調(diào)用
📖2.上層封裝
🔖3.示例
?4.總結(jié)?
📚四、文件描述符fd
📖1.工作原理
🔖示例
📖2.分配原則
📚五、重定向
📖1.常見的重定向
📖2.本質(zhì)
📖3.dup2系統(tǒng)調(diào)用
🔖語法?
🔖示例
📚六、總結(jié)
📚前言:一切皆文件?
在正式開始文件操作的介紹之前,我們先來解決一個(gè)問題,什么是文件?
我們常見的文件有:文本文件(如.txt,.cpp),二進(jìn)制文件(如編譯后的可執(zhí)行文件),圖像文件等等,我們和這些文件打交道,無非就是對文件寫入和對文件讀取,然而我們是怎么實(shí)現(xiàn)對文件的寫入和讀取的呢?其實(shí)操作系統(tǒng)為我們提供了這一切,我們告訴系統(tǒng)要訪問哪個(gè)文件,調(diào)用系統(tǒng)提供的方法,就實(shí)現(xiàn)了對文件的操作。
但文件的概念并不僅僅局限于磁盤上的存儲內(nèi)容,在操作系統(tǒng)中,幾乎所有資源都可以通過類似“文件”的方式來進(jìn)行訪問和操作。無論是硬盤上的數(shù)據(jù),還是連接計(jì)算機(jī)的外設(shè)設(shè)備,操作系統(tǒng)都通過類似文件的機(jī)制來統(tǒng)一管理他們。這是操作系統(tǒng)設(shè)計(jì)的一個(gè)重要思想——一切皆文件。
在這個(gè)框架下,設(shè)備(如鍵盤、鼠標(biāo)、網(wǎng)絡(luò)接口、內(nèi)存等)不再是與文件不同的資源,而是被抽象為一種特殊類型的文件,通過統(tǒng)一的系統(tǒng)調(diào)用接口,我們可以像操作普通文件那樣,操作這些設(shè)備,這種設(shè)計(jì)方式使得我們能夠以一種一致的方式訪問硬件資源。
下面我們來介紹操作系統(tǒng)具體是如何對文件進(jìn)行操作,以及如何以“文件”的方式管理各種設(shè)備的。
📚一、C語言的文件接口
任何對文件的操作都可以看成對數(shù)據(jù)的訪問、讀取和寫入,系統(tǒng)為我們提供了這些操作的接口,下面我們就來看看C語言下的文件接口:
📖1.文件打開
🔖語法
C語言提供了標(biāo)準(zhǔn)庫函數(shù) fopen()?用于打開文件:
FILE *fopen(const char *filename, const char *mode);
① 參數(shù)1:filename,表示文件名,指定要打開的文件路徑,可以是絕對路徑也可以是相對路徑
② 參數(shù)2:mode,文件打開模式,指定打開文件的方式(文件操作的權(quán)限),常見的有:
? ? ?"r",只讀方式打開文件,文件必須存在
? ? ?"w",只寫方式打開文件,文件不存在則創(chuàng)建,存在則清空文件
? ? ?"a",追加模式,文件不存在則創(chuàng)建,存在則數(shù)據(jù)追加到文件末尾
? ? ?"rb",以二進(jìn)制模式讀取文件
? ? ?"rw",以二進(jìn)制模式寫入文件
③ 返回值類型:FILE*,文件指針,用于標(biāo)記當(dāng)前打開的文件
🔖本質(zhì)
fopen文件訪問其實(shí)是做了以下工作:
1. 定位當(dāng)前文件
我們打開一個(gè)文件的本質(zhì)其實(shí)是向系統(tǒng)申請指定文件的描述符(FILE*指針),通過這個(gè)描述符系統(tǒng)就能定位文件,才能完成后續(xù)的讀寫操作。所以對文件操作之前一定要先打開文件(其實(shí)就是獲取文件描述符)
在C語言中,文件描述符以指針的形式存在,FILE * 是一個(gè)指向文件對象的指針,它是一個(gè)結(jié)構(gòu)體,內(nèi)部包含了文件操作的狀態(tài)(如文件位置、訪問模式等)。
2.設(shè)置文件訪問模式
打開文件時(shí),需要指定文件的“訪問模式”(如讀取、寫入、追加等),這告訴操作系統(tǒng)你希望如何使用文件:是否允許讀取文件內(nèi)容,是否可以修改文件,文件是否追加數(shù)據(jù),如果文件不存在是否需要創(chuàng)建。
3.定位文件指針
當(dāng)文件被打開時(shí),操作系統(tǒng)會初始化一個(gè)文件指針,指示文件中當(dāng)前可以進(jìn)行讀寫操作的位置。在文件讀取或?qū)懭霑r(shí),文件指針會根據(jù)操作而前進(jìn)或后退。例如,當(dāng)你讀一個(gè)文件時(shí),文件指針會向前移動,直到讀到文件的末尾(EOF)。當(dāng)你寫一個(gè)文件時(shí),文件指針通常會向文件的結(jié)尾移動,或者在追加模式下繼續(xù)從文件的末尾寫入。?
🔖示例
FILE *fp = fopen("myfile", "w");if(!fp){printf("fopen error!\n"); // 訪問失敗返回空指針}
這里以"w"只寫的方式打開"myfile"文件(文件不存在則創(chuàng)建,存在則清空),并返回一個(gè)文件指針,?如果該文件沒有寫權(quán)限時(shí),打開失敗,返回空指針。
📖2.文件讀取
🔖語法
C語言提供了標(biāo)準(zhǔn)庫函數(shù) fread() 用于讀取文件數(shù)據(jù)到緩沖區(qū)中:
ssize_t fread(void *ptr, size_t size, size_t count, FILE *stream);
① 參數(shù)1:ptr,指向存儲讀取數(shù)據(jù)的緩沖區(qū)的指針,讀取的數(shù)據(jù)會存放到該緩沖區(qū)
② 參數(shù)2:size,讀取的單個(gè)數(shù)據(jù)元素的大小(單位為字節(jié))
③ 參數(shù)3:count,讀取的元素個(gè)數(shù)
④ 參數(shù)4:stream,文件指針(FILE *,就是前面 fopen 的返回值)?
⑤ 返回值類型:size_t,返回成功讀取的元素個(gè)數(shù)(count)
🔖示例
#include <stdio.h>
#include <stdlib.h>int main() {FILE *fp = fopen("numbers.dat", "rb");if (fp == NULL) {perror("Error opening file");return 1;}int numbers[100];size_t elementsRead = fread(numbers, sizeof(int), 100, fp);if (elementsRead != 100) {if (feof(fp)) {printf("Reached end of file.\n");} else {perror("Error reading file");}}for (size_t i = 0; i < elementsRead; i++) {printf("%d ", numbers[i]);}printf("\n");fclose(fp);return 0;
}
fread() 這里用于讀取 numbers.dat 文件的100個(gè)整數(shù),如果文件中少于100個(gè)整數(shù),fread() 會讀取到文件結(jié)束,并返回實(shí)際讀取的文件個(gè)數(shù)。
使用 feof() 檢查文件是否到達(dá)文件末尾,到達(dá)返回1,否則返回0。
📖3.文件寫入
C語言提供了標(biāo)準(zhǔn)庫函數(shù) fwrite()?用于文件寫入,與 fread() 相對應(yīng):
🔖語法
ssize_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
① 參數(shù)1:ptr,指向?qū)懭霐?shù)據(jù)指針,可以是數(shù)組、結(jié)構(gòu)體、字符串等
② 參數(shù)2:size,寫入的單個(gè)數(shù)據(jù)元素的大小(單位為字節(jié))
③ 參數(shù)3:count,寫入的元素個(gè)數(shù)
④ 參數(shù)4:stream,文件指針(FILE *,就是前面 fopen 的返回值)?
⑤ 返回值類型:size_t,返回成功寫入的元素個(gè)數(shù)(count)
可以看出 fwrite() 和 fread() 的函數(shù)構(gòu)造是一樣的。
🔖示例
#include <stdio.h>
#include <stdlib.h>int main() {FILE *fp = fopen("numbers.dat", "wb");if (fp == NULL) {perror("Error opening file");return 1;}int numbers[] = {1, 2, 3, 4, 5};size_t elementsWritten = fwrite(numbers, sizeof(int), 5, fp);if (elementsWritten != 5) {perror("Error writing file");}fclose(fp);return 0;
}
fwrite() 將整數(shù)數(shù)組 numbers 中的5個(gè)整數(shù)寫入文件 number.dat,如果寫入的元素個(gè)數(shù)小于預(yù)期,程序會打印錯(cuò)誤信息
??注意:
寫入文件時(shí)必須使用 "wb" 或 "w" 模式打開文件;使用 "wb" 或 "w" 打開文件時(shí),會清空文件的現(xiàn)有內(nèi)容(如果文件已經(jīng)存在)。如果你希望追加數(shù)據(jù),而不是覆蓋原文件,可以使用 "a" 或 "ab"模式打開文件。
📖4.文件關(guān)閉
fclose() 函數(shù)用于關(guān)閉 fopen() 打開的文件,并釋放文件的資源。關(guān)閉文件后,不能再通過該文件指針訪問文件內(nèi)容:
🔖語法
#include <stdio.h>int fclose(FILE *stream);
① 參數(shù):stream,指向FILE對象的指針,表示要關(guān)閉的文件
② 返回值類型:int,關(guān)閉成功返回0,失敗返回 EOF,可以通過 perror() 獲取錯(cuò)誤信息。
🔖作用
1.沖刷緩沖區(qū):如果文件是以寫方式打開的,fclose() 會保證緩沖區(qū)的數(shù)據(jù)被刷新到磁盤,如果有任何未寫入的數(shù)據(jù),都會被寫入目標(biāo)文件。
2.釋放資源:關(guān)閉文件后,操作系統(tǒng)會釋放與該文件相關(guān)的資源(例如文件描述符)。這對于防止資源泄漏非常重要。
3.文件指針失效:文件關(guān)閉后,文件指針不再有效。若再次訪問該指針,將導(dǎo)致未定義行為。
📖5.默認(rèn)流指針
fopen()返回的文件指針我們又稱之為文件流指針,因?yàn)槲募举|(zhì)上是一個(gè)數(shù)據(jù)流,它可以從文件中讀取數(shù)據(jù),也可以向文件中寫入數(shù)據(jù)。在這種抽象下,文件操作就像處理一個(gè)數(shù)據(jù)流,而文件流指針則是指向這個(gè)流的一個(gè)句柄。
在C語言中,有三個(gè)默認(rèn)的文件流指針,分別指向標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出,使得我們無需顯式地打開文件即可進(jìn)行常見的文件操作:
① stdin 是標(biāo)準(zhǔn)輸入流,指向鍵盤輸入,可以使用 scanf() 從標(biāo)準(zhǔn)輸入讀取數(shù)據(jù),也可以通過這個(gè)流指針,將鍵盤輸入的數(shù)據(jù)存儲到磁盤文件中;
② stdout 是標(biāo)準(zhǔn)輸流,指向終端或控制臺,可以使用 printf() 將數(shù)據(jù)輸出到標(biāo)準(zhǔn)輸出,也可以通過流指針將磁盤文件內(nèi)容輸出到標(biāo)準(zhǔn)輸出中;
#include <stdio.h>#include <string.h>int main(){const char *msg = "hello fwrite\n";fwrite(msg, strlen(msg), 1, stdout);printf("hello printf\n");fprintf(stdout, "hello fprintf\n");return 0;}
③ stderror 是標(biāo)準(zhǔn)錯(cuò)誤流,用于輸出錯(cuò)誤信息。也指向終端或控制臺。
📚二、系統(tǒng)調(diào)用接口
在操作系統(tǒng)中,文件操作不僅僅是通過標(biāo)準(zhǔn)庫函數(shù)如 fopen()
, fread()
, fwrite()
, 和 fclose()
實(shí)現(xiàn)的,還可以通過系統(tǒng)調(diào)用接口直接進(jìn)行。系統(tǒng)調(diào)用提供了低級別、直接的操作系統(tǒng)資源訪問方式,包括對文件的操作。這些系統(tǒng)調(diào)用通常用于底層編程,它們繞過標(biāo)準(zhǔn)庫函數(shù),直接與操作系統(tǒng)內(nèi)核交互。
📖1.文件打開
在 Linux 系統(tǒng)中,文件的打開操作是通過系統(tǒng)調(diào)用 open()
完成的。open()
函數(shù)會返回一個(gè)文件描述符(而不是 FILE*
指針),這是操作文件的基礎(chǔ):
🔖語法
int open(const char *pathname, int flags, mode_t mode);
① 參數(shù)1:pathname,文件路徑,指定要打開的文件。
② 參數(shù)2:flags,指定文件的打開模式,如:
? ? ?O_RDONLY
:只讀模式
? ? ?O_WRONLY
:只寫模式
? ? ?O_RDWR
:讀寫模式
? ? ?O_CREAT
:如果文件不存在則創(chuàng)建
? ? ?O_APPEND
:追加模式
③ 參數(shù)3:mode,文件的默認(rèn)權(quán)限設(shè)置,僅在創(chuàng)建新文件時(shí)有效,通常為0644權(quán)限位:
? ? ?0表示當(dāng)前數(shù)字為八進(jìn)制,我們在設(shè)置權(quán)限時(shí),要考慮三類用戶:所有者,所有組以及其他用戶
? ? ?644表示所有者權(quán)限為可讀可寫不可執(zhí)行,所有組和其他用戶僅可讀,不可寫不可執(zhí)行。
④ 返回值:int,打開成功時(shí)返回一個(gè)非負(fù)整數(shù),表示文件描述符;打開失敗返回-1。int類型的文件描述符和FILE*指針作用一樣,都可以指向文件,前者可以看作數(shù)組下標(biāo),后者作為指針指向。
🔖示例
#include <fcntl.h>
#include <unistd.h>int main()
{ int fd1 = open("myfile_1", O_RDONLY); // mode可缺省int fd2 = open("myfile_2", O_WRONLY, 0664);
}
📖2.文件讀取
系統(tǒng)調(diào)用 read()?用于從已打開的文件描述符中讀取數(shù)據(jù):
🔖語法
ssize_t read(int fd, void *buf, size_t count);
① 參數(shù)1:fd,文件描述符,通過 open()
獲取。
② 參數(shù)2:buf,緩沖區(qū),存儲讀取的數(shù)據(jù)。
③ 參數(shù)3:要讀取的字節(jié)數(shù)。
④ 返回值:ssize_t,成功時(shí),返回實(shí)際讀取的字節(jié)數(shù);失敗時(shí),返回 -1(所以這里不能使用size_t作為返回值,而是ssize_t)
🔖示例
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(){int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}const char *msg = "hello bit!\n";char buf[1024];while(1){ssize_t s = read(fd, buf, strlen(msg));//類比writeif(s > 0){printf("%s", buf);}else{break;}}close(fd);return 0;}
📖3.文件寫入
系統(tǒng)調(diào)用 write()
用于將數(shù)據(jù)寫入文件:
🔖語法
ssize_t write(int fd, const void *buf, size_t count);
🔖示例
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(){umask(0);int fd = open("myfile", O_WRONLY|O_CREAT, 0644);if(fd < 0){perror("open");return 1;}int count = 5;const char *msg = "hello bit!\n";int len = strlen(msg);while(count--)write(fd, msg, len);close(fd);return 0;}
?umask()是Linux中設(shè)置權(quán)限掩碼的系統(tǒng)調(diào)用,用于控制文件創(chuàng)建的默認(rèn)權(quán)限,調(diào)用 umask(0)
將文件創(chuàng)建掩碼設(shè)置為 0
,意味著沒有權(quán)限被去除,系統(tǒng)會允許最大權(quán)限的創(chuàng)建。
如果調(diào)用 umask(002)
,則創(chuàng)建的文件會去掉 2 (即 0002
),那么文件權(quán)限將變成 664
,目錄權(quán)限將變成 775
,即去除其他用戶的寫權(quán)限。
📖4.文件關(guān)閉
系統(tǒng)調(diào)用 close()
用于關(guān)閉打開的文件描述符,釋放相關(guān)資源:
🔖語法
int close(int fd);
① 參數(shù):fd,文件描述符,通過 open()
獲取。
② 返回值:int,成功時(shí),返回 0;
失敗時(shí),返回 -1
?。
作用與fclose相同,也是沖刷緩沖區(qū)以及釋放資源。
📚三、底層調(diào)用&上層封裝
?C語言標(biāo)準(zhǔn)庫函數(shù)與系統(tǒng)調(diào)用函數(shù)都可以實(shí)現(xiàn)對文件的訪問操作,那么它們之間有什么關(guān)聯(lián)呢?
?C語言標(biāo)準(zhǔn)庫函數(shù)是對系統(tǒng)調(diào)用的上層封裝
📖1.底層調(diào)用
底層調(diào)用即系統(tǒng)調(diào)用,是操作系統(tǒng)提供的接口,允許用戶程序與操作系統(tǒng)內(nèi)核進(jìn)行交互。當(dāng)程序需要進(jìn)行文件操作時(shí),實(shí)際上是通過調(diào)用操作系統(tǒng)內(nèi)核提供的系統(tǒng)調(diào)用接口完成的,常見的系統(tǒng)調(diào)用接口有 open(), write(), read(), close() 等,這些系統(tǒng)調(diào)用直接與操作系統(tǒng)的文件系統(tǒng)進(jìn)行交互。
📖2.上層封裝
C語言標(biāo)準(zhǔn)庫函數(shù) fopen(), fread(), fwrite(), fclose()?是對操作系統(tǒng)提供的系統(tǒng)調(diào)用的封裝,它們提供了更高層次的接口,使得使用者不需要直接與操作系統(tǒng)底層交互,能夠更便捷地進(jìn)行文件操作。標(biāo)準(zhǔn)庫函數(shù)內(nèi)部實(shí)現(xiàn)了文件描述符的管理、緩沖區(qū)的操作等,屏蔽了底層的細(xì)節(jié)。
🔖3.示例
open()
是一個(gè)系統(tǒng)調(diào)用,直接與操作系統(tǒng)交互,返回一個(gè)文件描述符。這個(gè)文件描述符可以用于進(jìn)一步的 read()
、write()
等操作。其實(shí)現(xiàn)較為底層,涉及操作系統(tǒng)的文件系統(tǒng)和內(nèi)存管理。
fopen()
是 C 語言標(biāo)準(zhǔn)庫函數(shù),它的內(nèi)部實(shí)現(xiàn)使用了 open()
系統(tǒng)調(diào)用來打開文件。除了 open()
,fopen()
還管理了緩沖區(qū)的初始化等工作,簡化了文件操作過程。fopen()
返回的是一個(gè)文件指針(FILE*
),它在標(biāo)準(zhǔn)庫內(nèi)部使用該指針來進(jìn)行文件操作,而不是直接暴露文件描述符。
?4.總結(jié)?
特性 | 系統(tǒng)調(diào)用 open() / read() / write() | 系統(tǒng)調(diào)用 open() / read() / write() |
功能 | 直接與操作系統(tǒng)交互,底層文件操作 | 提供高層接口,封裝底層系統(tǒng)調(diào)用 |
返回值 | 文件描述符(int) | 文件指針(FILE* ) |
管理緩沖區(qū) | 不負(fù)責(zé)緩沖區(qū)管理 | 自動管理文件緩沖區(qū)(提高效率) |
使用難度 | 較低層,涉及操作系統(tǒng)管理 | 較高層,易于使用,屏蔽底層細(xì)節(jié) |
適用場景 | 需要精細(xì)控制文件操作的底層程序 | 一般的文件操作,簡潔高效的接口 |
📚四、文件描述符fd
文件描述符(File Descriptor,簡稱fd)是操作系統(tǒng)用來表示已打開文件的整數(shù)。它是系統(tǒng)用來跟蹤打開文件的標(biāo)識符,與標(biāo)準(zhǔn)流、系統(tǒng)調(diào)用的接口密切相關(guān)。
📖1.工作原理
每當(dāng)程序調(diào)用 open()
函數(shù)打開一個(gè)文件,操作系統(tǒng)會為該文件分配一個(gè)文件描述符。文件描述符是一個(gè)非負(fù)整數(shù),用于在后續(xù)的系統(tǒng)調(diào)用中標(biāo)識該文件。
操作系統(tǒng)通常會為每個(gè)進(jìn)程維護(hù)一個(gè)文件描述符表,其中每個(gè)文件描述符對應(yīng)一個(gè)打開的文件或設(shè)備。在 Linux 系統(tǒng)中,文件描述符通常從 0 開始分配。0、1、2 是系統(tǒng)默認(rèn)的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出流,而其他文件描述符則用于指向程序顯式打開的文件。
?
🔖示例
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>int main() {int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("Error opening file");return 1;}// 使用文件描述符fd讀取文件內(nèi)容char buffer[100];ssize_t bytesRead = read(fd, buffer, sizeof(buffer));if (bytesRead > 0) {write(1, buffer, bytesRead); // 輸出到標(biāo)準(zhǔn)輸出}close(fd); // 關(guān)閉文件描述符return 0;
}
在這個(gè)例子中,程序通過 open()
獲取文件描述符 fd
,然后用 read()
讀取文件內(nèi)容,最后用 close()
關(guān)閉文件描述符。文件描述符 fd
在操作系統(tǒng)內(nèi)部對應(yīng)于打開的文件或設(shè)備,操作系統(tǒng)會根據(jù)它來執(zhí)行讀取操作。
📖2.分配原則
文件描述符的分配原則是怎么樣的呢?來看看下面這段代碼:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(){int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;}
此時(shí)fd是3,如果我將0或者2關(guān)閉呢:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(){close(0);//close(2);int fd = open("myfile", O_RDONLY);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);close(fd);return 0;}
發(fā)現(xiàn)此時(shí)fd為0(或者2),由此可以得到文件描述符fd的分配原則:
在files_struct數(shù)組當(dāng)中,找到當(dāng)前沒有被使用的 最小的一個(gè)下標(biāo),作為新的文件描述符。?
📚五、重定向
重定向(Redirection)是操作系統(tǒng)提供的一種機(jī)制,允許將程序的輸入和輸出從默認(rèn)設(shè)備(通常是終端或控制臺)重定向到其他設(shè)備或文件。重定向通常通過操作系統(tǒng)提供的文件描述符來實(shí)現(xiàn)。
例如還是上面那段代碼,我們關(guān)閉1:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>int main(){close(1);int fd = open("myfile", O_WRONLY|O_CREAT, 00644);if(fd < 0){perror("open");return 1;}printf("fd: %d\n", fd);fflush(stdout);close(fd);exit(0);}
此時(shí)我們發(fā)現(xiàn),本應(yīng)該輸出到顯示器上的內(nèi)容輸出到了文件myfile中,其中fd=1,這種現(xiàn)象叫做輸出重定向。常見的重定向有:>, >>, <:
📖1.常見的重定向
🔖>
(輸出重定向):
功能: 將命令的標(biāo)準(zhǔn)輸出重定向到一個(gè)文件中。如果目標(biāo)文件已經(jīng)存在,則會覆蓋文件內(nèi)容。
echo "Hello, World!" > output.txt
這會將 "Hello, World!" 輸出到 output.txt
文件中,覆蓋文件原有內(nèi)容。
🔖>>
(追加輸出重定向):
功能: 將命令的標(biāo)準(zhǔn)輸出追加到文件末尾。如果目標(biāo)文件不存在,則會創(chuàng)建文件。
echo "New line of text" >> output.txt
這會將 "New line of text" 追加到 output.txt
文件的末尾。
🔖<
(輸入重定向):
功能: 將文件的內(nèi)容作為標(biāo)準(zhǔn)輸入傳遞給命令。
sort < input.txt
這會將 input.txt
文件的內(nèi)容傳遞給 sort
命令進(jìn)行排序。
這三種重定向符號是最常見的,用于控制數(shù)據(jù)流向文件或從文件讀取數(shù)據(jù)。在復(fù)雜的腳本或命令行操作中,它們非常有用,能夠幫助用戶將輸出存儲到文件中或從文件中讀取數(shù)據(jù)。
📖2.本質(zhì)
重定向的本質(zhì)是改變數(shù)據(jù)流的方向,每個(gè)文件描述符(如 0
, 1
, 2
)都關(guān)聯(lián)一個(gè) file_struct
(文件結(jié)構(gòu)體)。當(dāng)進(jìn)行重定向操作時(shí),操作系統(tǒng)需要首先清空當(dāng)前文件描述符的相關(guān)信息,然后修改文件描述符的指向,例如將2重定向到1時(shí):?
① 清除?2 指向的文件結(jié)構(gòu)體內(nèi)容;
② 修改 2 的指向,使其指向?1 所指向的文件結(jié)構(gòu)體內(nèi)容。
📖3.dup2系統(tǒng)調(diào)用
dup2
是一個(gè)用于文件描述符復(fù)制的系統(tǒng)調(diào)用,它的作用是將一個(gè)現(xiàn)有的文件描述符復(fù)制到另一個(gè)文件描述符上,替換掉目標(biāo)文件描述符原有的內(nèi)容。
🔖語法?
int dup2(int oldfd, int newfd);
① oldfd:源文件描述符,表示要復(fù)制的現(xiàn)有文件描述符;
② newfd:目標(biāo)文件描述符,表示復(fù)制到該文件描述符。如果該文件描述符已經(jīng)打開,則它會被關(guān)閉,然后復(fù)制 oldfd
的內(nèi)容。
🔖示例
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>int main()
{int fd = open("./tmp.txt", O_RDWR|O_CREAT, 0664);if (fd < 0)return -1;dup2(fd, 1);printf("i like linux!\n");return 0;
}
這里我們將標(biāo)準(zhǔn)輸出重定向到文件tmp.txt中,執(zhí)行結(jié)果:
📚六、總結(jié)
在 C 語言中,標(biāo)準(zhǔn)庫函數(shù)提供了較高層次的抽象,使得文件操作變得簡便易用。我們通過 fopen()
打開文件,利用 fread()
和 fwrite()
進(jìn)行讀寫操作,并通過 fclose()
關(guān)閉文件。這些操作的實(shí)現(xiàn)背后,實(shí)際上是依賴于操作系統(tǒng)提供的低級系統(tǒng)調(diào)用,如 open()
、read()
、write()
和 close()
。這些系統(tǒng)調(diào)用直接與操作系統(tǒng)內(nèi)核進(jìn)行交互,提供了更精細(xì)的控制。
通過對比系統(tǒng)調(diào)用與標(biāo)準(zhǔn)庫函數(shù)的使用場景,我們可以更清楚地理解它們各自的優(yōu)勢和適用范圍。標(biāo)準(zhǔn)庫函數(shù)封裝了底層細(xì)節(jié),適合一般的文件操作,而系統(tǒng)調(diào)用則提供了更低層次、更精細(xì)的操作,適合需要高性能和底層控制的場景。
以上就是【文件操作的藝術(shù)——從基礎(chǔ)到精通】的全部內(nèi)容,歡迎指正~?
碼文不易,還請多多關(guān)注支持,這是我持續(xù)創(chuàng)作的最大動力!??