【Linux從入門到精通】文件I/O操作(C語言vs系統調用)

文章目錄

一、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~?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/34808.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/34808.shtml
英文地址,請注明出處:http://en.pswp.cn/news/34808.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Golang bitset 基本使用

安裝&#xff1a; go get github.com/bits-and-blooms/bitset下面代碼把fmtx換成fmt就行 //------------基本操作------------//構建一個64bit長度的bitsetb : bitset.New(64)//放入一個數b.Set(10)fmtx.Println("add-10&#xff1a;", b.DumpAsBits()) // 0000000…

針對英特爾酷睿 CPU 優化,Canonical 發布 Ubuntu 實時內核

導讀Canonical 今天宣布針對支持時序協調運算&#xff08;TCC&#xff09;和時間敏感網絡&#xff08;IEEE TSN&#xff09;的英特爾酷睿處理器&#xff0c;推出優化版實時 Ubuntu 內核。 Canonical 于今年 2 月宣布&#xff0c;為購買 Ubuntu Pro 訂閱&#xff0c;使用代號為 …

OPENCV C++(七)霍夫線檢測+找出輪廓和外接矩形+改進旋轉

霍夫線檢測 vector<Vec2f> lines1;HoughLines(canny_mat, lines1, 1, CV_PI / 180.0,90 );//45可以檢測里面兩條線 80檢測出外邊兩條線 定義存放輸出線的向量 此向量輸出有<距離&#xff0c;角度> 因為檢測的原理就是在變換霍夫空間里面去檢測的&#xff0c;這里可…

ESP8266(RTOS SDK)內嵌網頁以實現WEB配網以及數據交互

【本文發布于https://blog.csdn.net/Stack_/article/details/131997098&#xff0c;未經允許不得轉載&#xff0c;轉載須注明出處】 1、執行make menuconfig&#xff0c;將http頭由512改為更大的值&#xff0c;否則用電腦瀏覽器訪問正常&#xff0c;但用手機瀏覽器訪問會因為ht…

基于weka手工實現K-means

一、K-means聚類算法 K均值聚類&#xff08;K-means clustering&#xff09;是一種常見的無監督學習算法&#xff0c;用于將數據集中的樣本劃分為K個不同的類別或簇。它通過最小化樣本點與所屬簇中心點之間的距離來確定最佳的簇劃分。 K均值聚類的基本思想如下&#xff1a; …

【快應用】list組件如何區分滑動的方向?

【關鍵詞】 list組件、滑動方向、scroll 【問題背景】 有cp反饋list這個組件在使用的時候&#xff0c;不知道如何區分它是上滑還是下滑。 【問題分析】 list組件除了通用事件之外&#xff0c;還提供了scroll、scrollbottom、scrolltop、scrollend、scrolltouchup事件&#x…

UIE在實體識別和關系抽取上的實踐

近期有做信息抽取的需求&#xff0c;UIE在信息抽取方面效果不錯。 模型準備 huggingface上下載UIE模型&#xff1a;PaddlePaddle/uie-base Hugging Face 點擊“Clone Repository”&#xff0c;確定git clone的鏈接 其中包含大文件&#xff0c;需要在windows安裝git-lfs&am…

九、多態(1)

本章概要 向上轉型回顧 忘掉對象類型 轉機 方法調用綁定產生正確的行為可擴展性陷阱&#xff1a;“重寫”私有方法陷阱&#xff1a;屬性與靜態方法 多態是面向對象編程語言中&#xff0c;繼數據抽象和繼承之外的第三個重要特性。 多態提供了另一個維度的接口與實現分離&…

C++_模板初階

在面向對象中&#xff0c;我們可以使用重載來實現多態。 但是問題在于&#xff0c;重載的函數僅僅是類型不同&#xff0c;代碼復用率比較低&#xff0c;只要有新的類型出現時&#xff0c;就要增加對應的函數&#xff1b;另一方面它的代碼可維護性比較低&#xff0c;一個出錯可…

java實現文件的下載

系統日志的獲取不可能每次都登錄服務器&#xff0c;所以在頁面上能夠下載系統運行的日志是必須的 如何來實現日志的下載&#xff0c;這樣的一個功能 前端我們用到的是window.open(...)這樣可以發送一個get請求到后臺 后臺接收到get請求之后&#xff0c;如何實現對文件的下載 R…

ubuntu中redis+mysql安裝使用

pip -V 回車&#xff08;大寫V&#xff09;&#xff1a;python包庫安裝路徑 python -m site: python查找路徑 1、redis ubuntu安裝redis System has not been booted with systemd as init system (PID 1). Cant operate&#xff1b;該問題是systemctl start redis報錯&#…

ZLMediaKit(webrtc)在linux上(CentOS7)部署與啟動

一.ZLMediaKit(webrtc)在CentOS7部署與啟動 # 1. 卸載舊版本 yum remove git # 2. 安裝 yum 源的 Git 版本 yum install -y git # 3. 查看版本 git version # 輸出 git version 1.8.3.1配置全局環境變量 # 1. 編輯配置文件 vim /etc/profile # 2. 在 /etc/profile 文件中末尾…

用 Rufus 制作 Ubuntu 系統啟動盤時,選擇分區類型為MBR還是GPT?

當使用 Rufus 制作 Ubuntu 系統啟動盤時&#xff0c;您可以根據您的需求選擇分區類型&#xff0c;MBR&#xff08;Master Boot Record&#xff09;還是 GPT&#xff08;GUID Partition Table&#xff09;。 MBR 是傳統的分區表格式&#xff0c;適用于大多數舊版本的操作系統和舊…

2023/08/13_____JMM JAVA Memory Model JAVA內存模型

JMM JAVA Memory Model java內存模型 作用&#xff1a;緩存一致性協議&#xff0c;用于定義數據讀寫的規則&#xff08;遵守&#xff0c;找到這個規則&#xff09; JMM定義了線程2工作內存和主內存之間的抽象關系&#xff1a;線程之間的共享變量存儲在主內存&#xff08;main …

TLS協議

目錄 什么是TLS協議&#xff1f; TLS的基本流程&#xff1f; 兩種密鑰交換算法&#xff1f; 基于ECDHE密鑰交換算法的TLS握手過程&#xff1f; 基于RSA密鑰交換算法的TLS握手過程&#xff1f; 基于RSA的握手和基于ECDHE的握手有什么區別&#xff1f; 什么是前向保密&…

tp6 v3微信退款

/*** Notes:退款* param $out_trade_no 支付時候訂單號&#xff08;order表 original_bn&#xff09;兩個參數選一個這個要選對* param $out_refund_no 退款訂單號* param $total 訂單金額* param $refund 退款金額* Time: 2023-08-10*/public function refundMoney($out_trade…

oracle的異常處理

oracle提供了預定義例外、非預定義例外和自定義例外三種類型。其中&#xff1a; l預定義例外用于處理常見的oracle錯誤&#xff1b; l非預定義例外用于處理預定義所不能處理的oracle錯誤&#xff1b; l自定義例外處理與oracle錯誤無關的其他情況。 Oracle代碼編寫過程中&am…

nginx反向代理與負載均衡

負載均衡依靠反向代理實現。nginx的代理分為七層代理與四層代理&#xff1a; 七層代理&#xff1a;七層代理的就是http請求和響應。七層代理是最常用的反向代理方式&#xff0c;只能配置在nginx配置文件的http模塊。而且配置方法名稱&#xff1a;upstream模塊&#xff0c;不能寫…

提升效率!Go語言開發者不可錯過的必備工具集合!

&#x1f337;&#x1f341; 博主貓頭虎 帶您 Go to Golang Language.??&#x1f341; &#x1f984; 博客首頁——貓頭虎的博客&#x1f390; &#x1f433;《面試題大全專欄》 文章圖文并茂&#x1f995;生動形象&#x1f996;簡單易學&#xff01;歡迎大家來踩踩~&#x1…

UDP數據報網絡編程(實現簡單的回顯服務器,客戶端)

回顯服務器表示客戶端發的是啥&#xff0c;服務器就返回啥&#xff0c;主要是為了熟悉UDP數據報網絡編程的基本步驟 對于程序的所有分析都寫到了代碼上 UDP回顯服務器代碼 package UdpEcho;import java.io.IOException; import java.net.DatagramPacket; import java.net.Dat…