在Linux下的文件IO操作

来源:https://www.cnblogs.com/Ye-Wei/archive/2022/04/04/16100500.html
-Advertisement-
Play Games

系統調用 系統調用: 操作系統提供給用戶程式調用的一組“特殊”介面,用戶程式可以通過這組“特殊”介面來獲得操作系統內核提供的服務 為什麼用戶程式不能直接訪問系統內核提供的服務為了更好地保護內核空間,將程式的運行空間分為 內核空間 和 用戶空間(也就是常稱的內核態和用戶態),它們分別運行在不同的級別上 ...


系統調用

系統調用: 操作系統提供給用戶程式調用的一組“特殊”介面,用戶程式可以通過這組“特殊”介面來獲得操作系統內核提供的服務  在這裡插入圖片描述

為什麼用戶程式不能直接訪問系統內核提供的服務為了更好地保護內核空間,將程式的運行空間分為 內核空間用戶空間(也就是常稱的內核態用戶態),它們分別運行在不同的級別上 在邏輯上是相互隔離的 。 因此 用戶進程在通常情況下不允許訪問內核數據 ,也無法使用內核函數,它們只能在用戶空間操作用戶數據 ,調用用戶空間的函數 。
在這裡插入圖片描述進行系統調用時 ,程式運行空間從用戶空間進入內核空間 ,處理完後再返回到用戶空間系統調用並不是直接與程式員進行交互的,它僅僅是一個通過軟中斷機制向內核提交請求,以獲取內核服務的介面 。在實際使用中程式員調用的通常是用戶編程介面 API 。
在這裡插入圖片描述Linux 中的系統調用包含在 Linux 的 libc 庫中,通過標準的 C 函數調用方法可以調用系統命令相對 API 更高了一層,它實際上是一個可執行程式,它的內部調用了用戶編程介面 (API )來實現相應的功能 。在這裡插入圖片描述內核如何區分和引用特定的文件

通過文件描述符 。文件描述符是一個非負的整數 ,是一個索引值 ,指向在內核中每個進程打開文件的記錄表 。 當打開一個現存文件或創建一個新文件時,內核就向進程返回一個文件描
述符;當需要讀寫文件時 也需要把文件描述符作為參數傳遞給相應的函數 。是一個非負的整數(通常是小整數),posix標準要求每次打開一個文件,必須使用當前進程最小的文件描述符號碼,因此打開一定是3.
一個進程啟動時 通常會打開 3 個文件:

標準輸入 標準輸入 標準出錯
STDIN_FILENO STDOUT_FILENO STDERR_FILENO
stdin stdout stderr
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#define MSG_STR "hello world\n"

int main(int argc, char **argv)
{
   printf("%s",MSG_STR);
   fputs(MSG_STR,stdout);
   write(STDOUT_FILENO,MSG_STR,strlen(MSG_STR));//標準輸出到屏幕MSG_STR
   return 0;
}

關於fputs和weitey

fputs(const char *s, FILE *stream);
write(int fd, const void *buf, size_t count);

文件的創建/打開

open() 函數用於打開或者創建文件。其在打開或者創建文件時可以指定文件的屬性及用戶的許可權等各種參數。

int open(const char *pathname, int flags);//打開已存在的文件
int open(const char *pathname, int flags, mode_t mode);//打開不存在的文件
//flags:read write操作文件的許可權
//mode:該文件在磁碟中 相對於 用戶的許可權
int open(const char *path, int oflag, [mode_t mode]);

args:
    const char *path: 文件路徑,可以是絕對,也可以是相對路徑 
    int oflag       : 文件打開的方式
                        - O_RDONLY 只讀打開
                        - O_WRONLY 只寫打開
                        - O_RDWR   可讀可寫打開
                        以上3種必選一個,以下4種可以任意選擇
                        - O_APPEND 追加打開,所寫數據附加到文件末
                        - O_CREAT  若此文件不存在則創建它
                        - O_EXCL   若文件存在則報錯返回 
                        - O_TRUNC  如果文件已存在,並且以只寫或可讀可寫方式打開
                        		   則將其長度截斷為0位元組
    [mode_t mode]   : 文件許可權,只有在創建文件時需要使用
    
return:
    文件描述符,非負整數是成功,-1是失敗
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc ,char **argv)
{
        int fd = -1;

        if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
        if(-1==fd)
        {
             printf("文件創建失敗\n");
        }
        else
        {
             printf("文件打開成功,fd = %d\n",fd);
        }
        return 0;
}

寫文件

當文件打開後,我們就可以向該文件寫數據了。在Linux系統中,用 write() 向打開的文件寫入數據,要使用這個函數,需要包含 #include <unistd.h> 。下麵是函數的說明:

ssize_t write(int fildes, const void *buf, size_t nbyte);

args:
    int fildes     : 寫入文件的文件描述符
    const void *buf: 寫入數據在記憶體空間存儲的地址
    size_t nbyte   : 期待寫入數據的最大位元組數
    
return:
    文件實際寫入的位元組數,非負整數是成功,-1是失敗(磁碟已滿或者超出該文件的長度等)
        if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
        if(rv == -1)
        {
             printf("寫入數據失敗\n");
        }
        else
        {
             printf("寫入數據成功\n");
        }

讀文件

同寫文件類似,要使用讀文件函數 read() ,需要包含 #include <unistd.h> 。下麵是函數的說明:

ssize_t read(int fildes, void *buf, size_t nbyte);

args:
    int fildes  : 讀取文件的文件描述符
    void *buf   : 讀取數據在記憶體空間存儲的地址
    size_t nbyte: 期待讀取數據的最大位元組數
    
return:
    文件實際讀取的位元組數,非負整數是成功,-1是失敗
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


#define  BUFSIZE        1024
#define  MSG_STR  "i love  linux\n"


int main(int argc ,char **argv)
{
        int     fd = -1;
        int     rv = -1;
        char    buf[BUFSIZE];

        if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
        if(fd==-1)
       	{
            printf("文件創建失敗\n");
        }
        else
        {
            printf("文件打開成功,fd = %d\n",fd);
        }

        if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
        if(rv == -1)
        {
             printf("寫入數據失敗\n");
        }
       	else
        {
             printf("寫入數據成功\n");
        }
		
        memset(buf,0,sizeof(buf));//把buf的值為0,
		if((rv = read(fd,buf,sizeof(buf)))<0)
        if(rv == -1)
         {
             printf("讀寫失敗\n");
         }
         else
         {
             printf("讀寫成功\n");
             goto cleanup;
         }

        printf("讀寫的數據 %d\n %s\n",rv,buf);
        return 0;
}    

但是這樣讀出是空的,所以要使用lseek文件偏離量,通俗來說:就是改變游標的位置,從哪裡讀數據.
memset函數
memset是電腦中C/C++語言初始化函數。作用是將某一塊記憶體中的內容全部設置為指定的值, 這個函數通常為新申請的記憶體做初始化工作。

void *memset(void *s, int ch, size_t n);
	str  -- 指向要填充的記憶體塊。
	c 	 -- 要被設置的值。該值以 int 形式傳遞,但是函數在填充記憶體塊時是使用該值的無符號字元形式。
	n	 -- 要被設置為該值的字元數。

文件的偏移量
在每個打開的文件中都有一個文件的偏移量,文件的偏移量會根據文件的讀寫而改變位置。我們可以通過 lseek() 函數來調整文件的偏移量。預設情況下,新打開文件的文件偏移量在文件的開始。同 write() 和 read() 函數類似,要使用這個函數,需要包含 #include <unistd.h> 。下麵是函數的說明:

off_t lseek(int fildes, off_t offset, int whence);

args:
    int fildes  : 修改文件的文件描述符
    off_t offset: 文件偏移量移動的距離
    int whence  : 文件偏移量的基址
                    - SEEK_SET 文件開始處
                    - SEEK_CUR 文件當前位置
                    - SEEK_END 文件結束處
    
return:
    當前文件指針的位置,非負整數是成功,-1是失敗
lseek(fd , 0 ,SEEK_SET);//將文件偏移量設置到文件開始第一個位元組上
lseek(fd , -1 ,SEEK_END);//將文件偏移量設置在文件倒數第一個位元組上

在數據寫入成功後,把文件偏移量移到文件開始第一行位元組上

 if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
 if(rv == -1)
 {
     printf("寫入失敗\n");
 }
 else
 {
     printf("寫入數據成功\n");
     goto cleanup;
 }
 lseek(fd,0,SEEK_SET);

關閉文件
當文件不再被使用時,可以調用 close() 函數來關閉被打開的文件。 除了用 close() 顯示地關閉文件外,通過結束進程也能隱式地關閉被該進程打開的所有文件。要使用該函數,需要包含 #include <unistd.h> 。下麵是函數的說明:

int close(int fildes);

args:
   int fildes: 要關閉文件的文件描述符
   
return:
    文件關閉狀態,0是成功,-1是失敗

代碼實現

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


#define  BUFSIZE        1024
#define  MSG_STR  "i love  linux\n"


int main(int argc ,char **argv)
{
        int     fd = -1;
        int     rv = -1;
        char    buf[BUFSIZE];

        if((fd = open("test.txt",O_CREAT|O_RDWR,0666))<0)
        if(fd==-1)
       	{
              printf("文件創建失敗\n");
              goto cleanup;
        }	
        else
        {
              printf("文件打開成功,fd = %d\n",fd);
        }

        if((rv = write(fd, MSG_STR,strlen(MSG_STR)))<0)
        if(rv == -1)
        {
               printf("寫入數據失敗\n");
               goto cleanup;
        }
       	else
        {
               printf("寫入數據成功\n");
        }
		lseek(fd,0,SEEK_SET);
		
        memset(buf,0,sizeof(buf));
        if((rv = read(fd,buf,sizeof(buf)))<0)
        if(rv == -1)
        {
            printf("讀寫失敗\n");
            goto cleanup;
        }
        else
        {
            printf("讀寫成功\n");
        }

        printf("讀寫的數據 %d\n %s\n",rv,buf);

cleanup:
        close(fd);
        return 0;
}

strerror函數
C 庫函數 char *strerror(int errnum) 從內部數組中搜索錯誤號 errnum,並返回一個指向錯誤消息字元串的指針。strerror 生成的錯誤字元串取決於開發平臺和編譯器。

char *strerror(int errnum)

進一步代碼實現

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>


#define  BUFSIZE        1024
#define  MSG_STR        "hello world\n"


int main(int argc ,char **argv)
{
        int     fd = -1;
        int     rv = -1;
        char    buf[BUFSIZE];


        fd = open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0666);
        if(fd < 0)
        {
                perror("創建/打開文件失敗");
                return 0;
        }
        printf("打開文件成功 [%d]\n",fd);

        if((rv = write(fd,MSG_STR,strlen(MSG_STR))) < 0)
        {
                printf("文件寫入失敗:%s\n",strerror(errno));
                goto cleanup;
        }
        lseek(fd, 0 ,SEEK_SET);

        memset(buf, 0 ,sizeof(buf));
        if((rv = read(fd,buf,sizeof(buf))) < 0)
        {
                printf("讀文件失敗:%s\n",strerror(errno));
                goto cleanup;
        }
        printf("從文件讀出%d數據:%s\n",rv,buf);

cleanup:
        close(fd);
        return 0;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 《零基礎學Java》 事件監聽器 為按鈕等添加事件監聽器,事件監聽器的作用是在用戶單擊按鈕時,設置窗體要實現的功能。 動作事件監聽器 動作事件監聽器(AbstractAction)監聽器是Swing中比較常用的事件監聽器,很多最近的動作都會使用它監聽(比如:按鈕被單擊)。 動作事件監聽器 動作事件監 ...
  • 最近實現了一個多端登錄的Spring Security組件,用起來非常絲滑,開箱即用,可插拔,而且靈活性非常強。我覺得能滿足大部分場景的需要。目前完成了手機號驗證碼和微信小程式兩種自定義登錄,加上預設的Form登錄,一共三種,現在開源分享給大家,接下來簡單介紹一下這個插件包。 DSL配置風格 切入正 ...
  • 目錄 一.簡介 二.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 OpenGL (ES) 學習路 ...
  • java使用AWT和Swing相關的類可以完成圖形化界面編程,其中AWT的全稱是抽象視窗工具集(Abstract Window Toolkit),它是sun公司最早提供的GUI庫,這個GUI庫提供了一些基本功能,但這個GUI庫的功能比較有限,所以後來sun公司又提供了Swing庫。通過使用AWT和S ...
  • 閱文時長 | 0.54分鐘 字數統計 | 876字元 主要內容 | 1、引言&背景 2、部分通用設計代碼 3、聲明與參考資料 『.Net MVC實現全局異常捕捉返回通用異常頁面的一種方式』 編寫人 | SCscHero 編寫時間 | 2022/4/3 PM11:54 文章類型 | 系列 完成度 | ...
  • 閱文時長 | 1.15分鐘 字數統計 | 1844.8字元 主要內容 | 1、引言&背景 2、部分設計分享 3、聲明與參考資料 『.Net MVC實現角色-API許可權驗證的一種方式』 編寫人 | SCscHero 編寫時間 | 2022/3/27 PM9:31 文章類型 | 系列 完成度 | 已完成 ...
  • 三類設計模式的對比 英文名 設計模式數量 用途、意義 創建型模式 Creational Pattern 5 創建型模式關註對象的創建過程,將對象的創建和使用分離,降低系統耦合度,讓設計方案更易於修改和擴展 結構型模式 Structural Pattern 7 結構型模式關註如何將類或對象組織在一起, ...
  • 問題描述 考慮這樣一個需求:畫布上的對象支持手勢操作,手勢操作模式有平移、縮放、旋轉,對象可以支持一種或多種手勢,如何定義這個手勢操作模式? 就像文件的許可權一樣,只讀、只寫、讀寫,手勢操作也可以這樣設計。將手勢操作模式定義為簡單的枚舉類型是不夠的,我們需要表示不同模式的組合,需要支持位運算,因此每個 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...