Linux系統(tǒng)編程(一) ------ 文件操作函數(shù)

文件操作

打開(kāi)文件

1.使用open()函數(shù)打開(kāi)和創(chuàng)建文件

  • 手冊(cè)文件 man 2 open

函數(shù)頭文件及函數(shù)原型
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

函數(shù)參數(shù):

  pathname:待打開(kāi)文件的絕對(duì)路徑和文件名。

  flags:打開(kāi)的旗標(biāo)類型,或稱模式,
  O_RDONLY      只讀模式打開(kāi)文件
  O_WRONLY      只寫(xiě)模式打開(kāi)文件
  O_RDWR        讀寫(xiě)模式打開(kāi)文件
  O_CREAT       若欲打開(kāi)的文件不存在則自動(dòng)建立該文件
  O_TRUNC       若文件存在并且以可寫(xiě)的方式打開(kāi)時(shí), 此旗標(biāo)會(huì)令文件長(zhǎng)度清為0, 
                而原來(lái)存于該文件的資料也會(huì)消失。
  O_EXCL        如果O_CREAT 也被設(shè)置, 此指令會(huì)去檢查文件是否存在。 
                文件若不存在則建立該文件,否則將導(dǎo)致打開(kāi)文件錯(cuò)誤. 
                此外, 若O_CREAT 與O_EXCL 同時(shí)設(shè)置,并且欲打開(kāi)的文件為符號(hào)連接, 
                則會(huì)打開(kāi)文件失敗。

  參數(shù)mode僅在flags中含有O_CREAT時(shí)有效,設(shè)定新建文文件的打開(kāi)權(quán)限,有下列數(shù)種組合,
  S_IRWXU             00700 權(quán)限,代表該文件所有者具有可讀、可寫(xiě)及可執(zhí)行的權(quán)限。
  S_IRUSR 或S_IREAD,  00400 權(quán)限,代表該文件所有者具有可讀取的權(quán)限。
  S_IWUSR 或S_IWRITE, 00200 權(quán)限,代表該文件所有者具有可寫(xiě)入的權(quán)限。
  S_IXUSR 或S_IEXEC,  00100 權(quán)限,代表該文件所有者具有可執(zhí)行的權(quán)限。
  S_IRWXG             00070 權(quán)限,代表該文件用戶組具有可讀、可寫(xiě)及可執(zhí)行的權(quán)限。
  S_IRGRP             00040 權(quán)限,代表該文件用戶組具有可讀的權(quán)限。
  S_IWGRP             00020 權(quán)限,代表該文件用戶組具有可寫(xiě)入的權(quán)限。
  S_IXGRP             00010 權(quán)限,代表該文件用戶組具有可執(zhí)行的權(quán)限。
  S_IRWXO             00007 權(quán)限,代表其他用戶具有可讀、可寫(xiě)及可執(zhí)行的權(quán)限。
  S_IROTH             00004 權(quán)限,代表其他用戶具有可讀的權(quán)限。
  S_IWOTH             00002 權(quán)限,代表其他用戶具有可寫(xiě)入的權(quán)限。
  S_IXOTH             00001 權(quán)限,xit代表其他用戶具有可執(zhí)行的權(quán)限。

函數(shù)返回值: 打開(kāi)文件成功,返回一個(gè)文件描述符 >2;打開(kāi)失敗,返回-1。

提示:使用 access()作用戶認(rèn)證方面的判斷要特別小心, 例如在access()后再作open()空文件可能會(huì)造成系統(tǒng)安全上的問(wèn)題。

2.使用create()函數(shù)創(chuàng)建并打開(kāi)文件

函數(shù)原型

     int creat(const char *pathname, mode_t mode);
     相當(dāng)于使用調(diào)用方式,
     open(const char *pathname, (O_CREAT|O_WRONLY|O_TRUNC));

函數(shù)參數(shù):

  pathname   待打開(kāi)文件的絕對(duì)路徑和文件名。

  mode       新創(chuàng)建文件的權(quán)限,見(jiàn)上面open()

函數(shù)返回值:若成功會(huì)返回新的文件描述符,若有錯(cuò)誤發(fā)生則會(huì)返回-1。

提示:creat()無(wú)法建立特別的裝置文件,如果需要請(qǐng)使用mknod()。

讀寫(xiě)文件

1.使用read()函數(shù)從文件中讀取數(shù)據(jù)

  • 手冊(cè)文件 man 2 read

函數(shù)頭文件及函數(shù)原型
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

函數(shù)參數(shù):

 fd      文件指針,提供數(shù)據(jù)的文件的文件描述符,讀取的數(shù)據(jù)的來(lái)源。

 buf     讀到的數(shù)據(jù)所存放的內(nèi)存空間的起始地址,同時(shí)文件的當(dāng)前讀寫(xiě)位置向后移。

 count   想要讀取的數(shù)據(jù)的字節(jié)數(shù),也是提供的存儲(chǔ)空間字節(jié)數(shù)。

函數(shù)說(shuō)明及返回值: read()會(huì)把參數(shù)fd 所指的文件傳送count 個(gè)字節(jié)到buf 指針?biāo)傅膬?nèi)存中(暨在[0,count]區(qū)間變化)。

1.若參數(shù)count 為0,則read()不會(huì)有作用并返回0。
2.成功時(shí),返回值為實(shí)際讀取到的字節(jié)數(shù)。
3.如果返回0,表示已到達(dá)文件尾,暨碰到了EOF或是無(wú)可讀取的數(shù)據(jù)。
4.此外文件讀寫(xiě)位置會(huì)隨讀取到的字節(jié)移動(dòng)。
5.有錯(cuò)誤發(fā)生時(shí)則返回-1,而文件讀寫(xiě)位置則無(wú)法預(yù)測(cè)。
提示:

read()函數(shù)負(fù)責(zé)從文件句柄中讀取指定數(shù)量的字節(jié),并將這些字節(jié)放在標(biāo)量型變量中。read()函數(shù)和標(biāo)準(zhǔn)I/O函數(shù)fread()相同的方式處理I/O緩沖的。為了提高效率,read()函數(shù)并不是一次讀取一個(gè)字節(jié),而是讀取一塊數(shù)據(jù)并保存到臨時(shí)存儲(chǔ)區(qū)中。然后,C的fread函數(shù)與Perl的read函數(shù)會(huì)從臨時(shí)緩沖區(qū)將數(shù)據(jù)一次一個(gè)字節(jié)地傳送給程序。print()函數(shù)(而不是write()函數(shù)負(fù)責(zé)輸出read()函數(shù)返回的實(shí)際字節(jié)。print()函數(shù)類似于C中的fwrite()函數(shù)。

附加:如果順利 read()會(huì)返回實(shí)際讀到的字節(jié)數(shù),最好能將返回值與參數(shù)count 作比較,若返回的字節(jié)數(shù)比要求讀取的字節(jié)數(shù)少,則
1. 讀取普通文件時(shí),讀到文件末尾還不夠 nbytes 字節(jié)。例如:如果文件只有 30 字節(jié),
而我們想讀取 100字節(jié),那么實(shí)際讀到的只有 30 字節(jié),read 函數(shù)返回 30 。
此時(shí)再使用 read 函數(shù)作用于這個(gè)文件會(huì)導(dǎo)致 read 返回 0 。
2. 從終端設(shè)備(terminal device)讀取時(shí),一般情況下每次只能讀取一行。
3. 從網(wǎng)絡(luò)讀取時(shí),網(wǎng)絡(luò)緩存可能導(dǎo)致讀取的字節(jié)數(shù)小于 nbytes 字節(jié)。
4. 讀取 pipe 或者 FIFO 時(shí),pipe 或 FIFO 里的字節(jié)數(shù)可能小于 nbytes 。
5. 從面向記錄的設(shè)備讀取時(shí),某些面向記錄的設(shè)備(如磁帶)每次最多只能返回一個(gè)記錄。
6. 在讀取了部分?jǐn)?shù)據(jù)時(shí)被信號(hào)中斷。讀操作始于 cfo 。在成功返回之前,cfo 增加,
增量為實(shí)際讀取到的字節(jié)數(shù)。

2.使用write()函數(shù)向指定文件中寫(xiě)入數(shù)據(jù)
  • 手冊(cè)文件 man 2 write

函數(shù)頭文件及函數(shù)原型
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

函數(shù)參數(shù):

fd      待寫(xiě)入數(shù)據(jù)的文件的描述符

buf     寫(xiě)入數(shù)據(jù)的起始地址

count   待寫(xiě)入的數(shù)據(jù)的字節(jié)數(shù)

函數(shù)說(shuō)明及返回值: write()會(huì)把參數(shù)buf 所指的內(nèi)存寫(xiě)入count 個(gè)字節(jié)到參數(shù)fd 所指的文件內(nèi)。當(dāng)然,文件讀寫(xiě)位置也會(huì)隨之移動(dòng)。
如果順利會(huì)返回實(shí)際寫(xiě)入數(shù)據(jù)的字節(jié)數(shù),表示寫(xiě)了部分或者全部的數(shù)據(jù)。
當(dāng)有錯(cuò)誤發(fā)生時(shí),返回-1,我們要根據(jù)錯(cuò)誤的類型來(lái)處理。如果錯(cuò)誤為EINTR表示在寫(xiě)時(shí)出現(xiàn)了中斷錯(cuò)誤。如果為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問(wèn)題。

提示:對(duì)于普通文件,寫(xiě)操作始于 cfo 。如果打開(kāi)文件時(shí)使用了 O_APPEND,則每次寫(xiě)操作都將數(shù)據(jù)寫(xiě)入文件末尾。成功寫(xiě)入后,cfo 增加,增量為實(shí)際寫(xiě)入的字節(jié)數(shù)。

定位文件

預(yù)概念: 所有打開(kāi)的文件都有一個(gè)當(dāng)前文件偏移量(current file offset),以下簡(jiǎn)稱為 cfo。cfo 通常是一個(gè)非負(fù)整數(shù),用于表明文件開(kāi)始處到文件當(dāng)前位置的字節(jié)數(shù)。讀寫(xiě)操作通常開(kāi)始于 cfo,并且使 cfo 增大,增量為讀寫(xiě)的字節(jié)數(shù)。文件被打開(kāi)時(shí),cfo 會(huì)被初始化為 0,除非使用了 O_APPEND 。

使用lseek()函數(shù)定位指定已打開(kāi)文件的讀寫(xiě)指針

  • 手冊(cè)文件 man lseek

函數(shù)頭文件及函數(shù)原型
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

函數(shù)參數(shù):

fd 待重新定位讀寫(xiě)指針位置的文件的描述符

offset 讀寫(xiě)指針的偏移量(可正可負(fù)可為0)

whence 讀寫(xiě)指針的偏移位置
    SEEK_SET   相對(duì)文件首部偏移,文件偏移量將被設(shè)置為 offset。
    SEEK_CUR   相對(duì)文件當(dāng)前讀寫(xiě)位置偏移,文件偏移量將被設(shè)置為 cfo 加上 offset,
               offset 可以為正也可以為負(fù)。
    SEEK_END   相對(duì)文件尾部偏移,文件偏移量將被設(shè)置為文件長(zhǎng)度加上 offset,
               offset 可以為正也可以為負(fù)。

函數(shù)說(shuō)明及返回值: 每一個(gè)已打開(kāi)的文件都有一個(gè)讀寫(xiě)位置,當(dāng)打開(kāi)文件時(shí)通常其讀寫(xiě)位置是指向文件開(kāi)頭,若是以附加的方式打開(kāi)文件(如O_APPEND),則讀寫(xiě)位置會(huì)指向文件尾。當(dāng)read()或write()時(shí),讀寫(xiě)位置會(huì)隨之增加,lseek()便是用來(lái)控制該文件的讀寫(xiě)位置。參數(shù)fildes 為已打開(kāi)的文件描述詞,參數(shù)offset 為根據(jù)參數(shù)whence來(lái)移動(dòng)讀寫(xiě)位置的位移數(shù)。當(dāng)調(diào)用成功時(shí)則返回目前的讀寫(xiě)位置,也就是距離文件多少個(gè)字節(jié)數(shù)。若有錯(cuò)誤則返回-1。

例:

 將讀寫(xiě)位置移到文件開(kāi)頭時(shí): lseek(int fildes, 0, SEEK_SET);
 將讀寫(xiě)位置移到文件尾時(shí):   lseek(int fildes, 0, SEEK_END);
 想要取得目前文件位置時(shí):   lseek(int fildes, 0, SEEK_CUR);
提示:

1.Linux 系統(tǒng)不允許lseek()對(duì)tty 裝置作用,此項(xiàng)動(dòng)作會(huì)令lseek()返回ESPIPE。
2.如果參數(shù) fd(文件描述符)指定的是 pipe(管道)、FIFO 或者 socket,lseek 返回 -1 并且置 errno 為 ESPIPE。 對(duì)于普通文件(regular file),cfo 是一個(gè)非負(fù)整數(shù)。但對(duì)于特殊設(shè)備,cfo 有可能是負(fù)數(shù)。因此,我們不能簡(jiǎn)單地測(cè)試 lseek 的返回值是否小于 0 來(lái)判斷 lseek 成功與否,而應(yīng)該測(cè)試 lseek 的返回值是否等于 -1 來(lái)判斷 lseek 成功與否。
3.lseek 僅將 cfo 保存于內(nèi)核中,不會(huì)導(dǎo)致任何 I/O 操作。這個(gè) cfo 將被用于之后的讀寫(xiě)操作。
4.如果 offset 比文件的當(dāng)前長(zhǎng)度更大,下一個(gè)寫(xiě)操作就會(huì)把文件“撐大(extend)”。這就是所謂的在文件里創(chuàng)造"空洞(hole)”。沒(méi)有被實(shí)際寫(xiě)入文件的所有字節(jié)由重復(fù)的 0 表示。空洞是否占用硬盤空間是由文件系統(tǒng)(file system)決定的。

關(guān)閉文件

使用close函數(shù)關(guān)閉指定文件

  • 手冊(cè)文件 man close

函數(shù)頭文件及函數(shù)原型

 #include <unistd.h>
 int close(int fd);

函數(shù)參數(shù):

fd    為open()或creat()打開(kāi)的文件描述符。

函數(shù)說(shuō)明及返回值: 當(dāng)使用完已打開(kāi)的文件后若已不再需要?jiǎng)t可使用 close()關(guān)閉該文件, 而close()會(huì)讓數(shù)據(jù)寫(xiě)回磁盤, 并釋放該文件所占用的資源. 參數(shù)fd 為先前由open()或creat()所返回的文件描述詞.**返回值:若文件順利關(guān)閉則返回0, 發(fā)生錯(cuò)誤時(shí)返回-1.

提示:雖然在進(jìn)程結(jié)束時(shí),系統(tǒng)會(huì)自動(dòng)關(guān)閉已打開(kāi)的文件,但仍建議自行關(guān)閉文件,并確實(shí)檢查返回值。

綜合案例

// ./my-cp <src_file> <dst_file>

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

#define BUFFER_SIZE 100

int main(int argc, char *argv[])
{
    if(argc != 3)
    {
        printf("usage : %s <src_file> <dst_file>\n",
            argv[0]);
        return 1;
    }
    
    int src_fd = 0;
    int dst_fd = 0;
    int n = 0;
    char buf[BUFFER_SIZE] = {'\0'};
    char *src_file = argv[1];
    char *dst_file = argv[2];
    
    // 1.open
    // 1.1 以只讀方式打開(kāi)源文件
    if((src_fd = open(src_file, O_RDONLY)) == -1)
    {
        perror("open src error");
        return 1;
    }
    // 1.2 以只寫(xiě)方式打開(kāi)目的文件
    if((dst_fd = open(dst_file, 
            O_WRONLY | O_CREAT | O_TRUNC,
            S_IRUSR | S_IWUSR)) == -1)
    {
        perror("open dst error");
        return 1;
    }
    
    // 2. 循環(huán)從源文件中讀取數(shù)據(jù)寫(xiě)入到目的文件中
    // 直到讀到源文件的尾部為止
    // 2.1 read data from src_file
    // 2.2 write data to dst_file
    while((n = read(src_fd, buf, BUFFER_SIZE)) > 0)
    {
        write(dst_fd, buf, n);
    }
    
    // 3.close
    close(src_fd);
    close(dst_fd);

    return 0;
}

// 練習(xí):
// 實(shí)現(xiàn)一個(gè)相對(duì)完整版的cp程序,要求能夠判斷出目標(biāo)文件是否存在。
//  如果存在,給出提示是否覆蓋。
// 思路:
// 1.打開(kāi)源文件
// 2.判斷目的文件是否存在
// 3.如果目的文件存在,提示是否覆蓋
// 4.如果選擇覆蓋,則以只寫(xiě)的方式打開(kāi)文件,并截短文件內(nèi)容(O_TRUNC)
// 5.如果選擇不覆蓋,則提醒輸入新的保存文件名,并已只寫(xiě)方式打開(kāi)
// 6.循環(huán)讀取源文件內(nèi)容,寫(xiě)入到目的文件中
// 7.關(guān)閉已打開(kāi)的文件

// 思考題1:能否關(guān)閉標(biāo)準(zhǔn)輸入文件、標(biāo)準(zhǔn)輸出文件、標(biāo)準(zhǔn)出錯(cuò)文件?

參考資料

劉老師上課資料及網(wǎng)上前輩資料
計(jì)算機(jī)操作系統(tǒng)教程:介紹現(xiàn)代操作系統(tǒng)原理及應(yīng)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評(píng)論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,559評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,442評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,835評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,581評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,922評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評(píng)論 3 447
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,096評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,639評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,374評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,591評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,789評(píng)論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,196評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,524評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,322評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,554評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容