常用的文件IO操作



學習內容

    1)open函數的flag

  2)linux系統如何管理文件

  3)lseek詳解

  4)dup和dup2函數介紹

  5)標准IO庫介紹


如何查man手冊:man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查庫函數

1、open函數的flag

  大家有沒有發現open函數有兩個如下,

int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);

 多了一個mode,查看man手冊可以發現作用為O_CREAT創建了新文件時對文件的權限設置,如下圖:

常用的flag:

  1)讀寫權限:O_RDONLY(只讀), O_WRONLY(只寫), O_RDWR(可讀可寫).

  2)打開存在並有內容的文件時:O_APPEND(追加)、O_TRUNC(覆蓋)

  3)打開不存在的文件時:O_CREAT(覆蓋創建)、O_EXCL(確保文件創建) O_EXCL標志和O_CREAT標志來結合使用,沒有文件時創建文件,有這個文件時會報錯提醒我們。

  4)阻塞與非阻塞:O_NONBLOCK(非阻塞)我們打開一個文件默認就是阻塞式的,如果你希望以非阻塞的方式打開文件,則flag中要加O_NONBLOCK標志,只用於設備文件,而不用於普通文件。

  5)write非阻塞等待:O_SYNC  無O_SYNC時write只是將內容寫入底層緩沖區即可返回,在合適的時候會將buf中的內容一次性的同步到硬盤中。這種設計是為了提升硬件操作的性能和銷量,提升硬件壽命;但是有時候我們希望硬件不好等待,直接將我們的內容寫入硬盤中,這時候就可以用O_SYNC標志。

2、linux系統如何管理文件

  1)硬盤中的靜態文件和inode(i節點)

  硬盤中可以分為兩大區域:一個是硬盤內容管理表項,另一個是真正存儲內容的區域。操作系統訪問硬盤時是先去讀取硬盤內容管理表,從中找到我們要訪問的那個文件的扇區級別的信息,然后再用這個信息去查詢真正存儲內容的區域,最后得到我們要的文件。而這個管理表中以文件為單位記錄了各個文件的各種信息,每一個文件有一個信息列表(我們叫inode,i節點,其實質是一個結構體,這個結構體有很多元素,每個元素記錄了這個文件的一些信息,其中就包括文件名、文件在硬盤上對應的扇區號、塊號那些東西·····)

實用:大家格式化硬盤(U盤)時發現有:快速格式化和底層格式化。快速格式化非常快,格式化一個32GB的U盤只要1秒鍾,普通格式化格式化速度慢。這兩個的差異?其實快速格式化就是只刪除了U盤中的硬盤內容管理表(其實就是inode),真正存儲的內容沒有動。這種格式化的內容是有可能被找回的。

  2)內存中被打開的文件和vnode(v節點)

  一個程序的運行就是一個進程,我們在程序中打開的文件就屬於某個進程。每個進程都有一個數據結構用來記錄這個進程的所有信息(叫進程信息表),表中有一個指針會指向一個文件管理表,文件管理表中記錄了當前進程打開的所有文件及其相關信息。文件管理表中用來索引各個打開的文件的index就是文件描述符fd,我們最終找到的就是一個已經被打開的文件的管理結構體vnode。

3、lseek詳解

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

whence就是代表一個參考位置(用來表示文件開始處到文件當前位置的字節數)offset代表一個偏移量

參數 offset 的含義取決於參數 whence:
SEEK_SET,則返回的文件偏移量將被設置為 offset。
SEEK_CUR,則返回的文件偏移量將被設置為 cfo 加上 offset,
SEEK_END,則返回的文件偏移量將被設置為文件長度加上 offset,

  1)文件指針:當我們要對一個文件進行讀寫時,一定需要先打開這個文件,所以我們讀寫的所有文件都是動態文件。動態文件在內存中的形態就是文件流的形式。

  2)在動態文件中,我們會通過文件指針來表征這個正在操作的位置。所謂文件指針,就是我們文件管理表這個結構體里面的一個指針如上圖。所以文件指針其實是vnode中的一個元素。這個指針表示當前我們正在操作文件流的哪個位置。這個指針不能被直接訪問,linux系統用lseek函數來訪問這個文件指針。

  3)read和write函數都是從當前文件指針處開始操作的,所以當我們用lseek顯式的將文件指針移動后,那么再去read/write時就是從移動過后的位置開始的。

  4)用lseek計算文件長度

eg:return=lseek(fd,0,SEEK_END);//表示從文件末尾

  5)用lseek構建空洞文件,這個文件中有一段是空的

  6)使用實列:

首先我們看到a.txt里面全都是f,其實我們用O_APPEND就可以在后面追加內容,而我們用lseek來隨意位置追加內容

使用SEEK_END

lseek(fd,0,SEEK_END);//在write1前使用一次和O_APPEND同等效果,在write之后再使用一次即可的讀取出當前文件的長度

4、dup和dup2函數介紹

int dup(int oldfd); int dup2(int oldfd, int newfd);//返回值即為newfd

  1) 什么是文件共享:同一個文件(同一個文件指的是同一個inode,同一個pathname)被多個獨立的讀寫體(幾乎可以理解為多個文件描述符)去同時(一個打開尚未關閉的同時另一個去操作)操作。

  2) 進行文件描述符復制:dup並不能指定分配的新的文件描述符的數字,dup2系統調用修復了這個缺陷。dup返回的fd和原來的oldfd都指向oldfd打開的那個【動態文件】,操作這兩個fd實際操作的都是oldfd打開的那個文件。實際上構成了文件共享

  3) fd小於3的分別對應於,stdin(0)、stdout(1)、stderr(3),也就是標准輸入、標准輸出、標准錯誤。

  4)實現標准輸出的重定位實例

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#define FILEPATH    "a.txt"
int main(int argc,char *argv[])
{
    int fd=-1;
    int i=-1; //實際向文件中寫入的字節數
    int j=-1;//實際向文件中寫入的字節數
    int k;//用dup復制后的文件描述符
 
    char b[100]= "bbbbbadfghgdfh";
  //打開一個文件操作
    fd=open(FILEPATH, O_RDWR );
    if(-1==fd)
    {
        printf("文件打開失敗!\n");
        perror("open:");
        return 0;
    }
    else
    {
        printf("文件打開成功!fd=%d\n",fd);
    }
    close(1);   //關閉標准輸出文件描述符1
    k=dup(fd);  //實現重定位
    j=write(k,&b,20);
    if(-1==j)
    {
        printf("寫入文件失敗\n");
        perror("寫入");
        return 0;
    }
    printf("write succeed!\n");
    close(fd);
    return 0;
}

5、標准IO庫介紹

  1)標准IO和文件IO有什么區別:標准IO是C庫函數(也是通過調用API來完成操作的),而文件IO是linux系統的API(API類似於一種接口,是由操作系統提供的)

  2)常用標准IO函數介紹:fopen、fclose、fwrite、fread、ffulsh(刷新標准庫函數的緩存,直接寫進操作系統的緩沖區中)、fseek

  3)一個簡單的標准IO讀寫文件實例

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

#define FILEPATH    "a.txt"

int  main(int argc,char *argv[])
{
    FILE *fd = NULL;  //定義一個文件指針
    int ret = 0;
    char Wrtebuff[10]={"sdfgdjdytr"};
    fd=fopen(FILEPATH, "r+");
    if(NULL == fd)
    {
        printf("文件打開失敗!\n");
        perror("open");
        return 0;
    }
    ret = fwrite(Wrtebuff,1,sizeof(Wrtebuff),fd);
    if(ret<0)
    {
        perror("write");
        return 0;
    }
    printf("文件寫入成功!\n");
    
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM