1. 程式人生 > >C語言-讀寫檔案I/O

C語言-讀寫檔案I/O

C語言讀寫檔案的步驟一般是:
建立或開啟檔案 > 讀或寫檔案 > 關閉檔案
當然讀或寫的過程中還可以通過操作當前檔案偏移量來控制讀寫位置。
下面分別介紹這些函式。

標頭檔案:

#include <fcntl.h>
//此標頭檔案定義了以下oflag
O_RDONLY     //只讀0
O_WRONLY     //只寫1
O_RDWR       //讀寫2
//上面三個oflag必須選且只能選一個,下面是可選oflag
O_APPEND     //追加
O_CREAT      //若不存在則建立。使用此選項時需要第三個引數mode,用以指定新檔案訪問許可權位。
O_EXCL       //如果指定了O_CREAT,檔案存在則報錯,不存在則建立。測試檔案存在和建立檔案為原子操作。
O_TRUNC //若檔案存在,且以只寫或讀寫開啟,則將其長度截短為0。 O_NOCTTY //若pathname為終端裝置,則不將該裝置分配作為此程序的控制終端。 O_NONBLOCK //若pathname為FIFO、塊特殊檔案或字元特殊檔案,則為檔案的本次開啟操作和後續I/O操作設定非阻塞模式。 O_RSYNC //使每一個以檔案描述符作為引數的read操作等待,直至任何對檔案同一部分進行的未決寫操作都完成。 O_DSYNC //使每次write等待物理I/O操作完成,但是如果寫操作不影響讀取剛寫入的資料,則不等待檔案屬性被更新。 O_SYNC //使每次write等到物理I/O操作完成,包括由write操作引起的檔案屬性更新所需的I/O。
O_SYNC和O_DSYNC區別: O_SYNC:每次write後文件屬性更新完成,write才返回,不管write是否寫入新位元組或檔案內容是否被修改。 而O_DSYNC在write並不影響檔案資料內容時,檔案屬性不會變。

開啟:

#include <fcntl.h>
int open(const char *pathname, int oflag, ... /* mode_t mode */);
//成功返回檔案描述符,出錯返回-1

建立:

#include <fcntl.h>
int create(const char *pathname, mode_t mode)
; //成功返回檔案描述符,出錯返回-1 //相當於: open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode); //create後文件是隻寫方式開啟,若要讀,則需要先呼叫create、close,然後再open,也可以直接用下面的方式open: open(pathname, O_RDWR | O_CREAT | O_TRUNC, mode);

關閉:

#include <unistd.h>
int close(int filedes);
//成功返回0,出錯返回-1。當一個程序終止時,核心會自動關閉它所開啟的所有檔案。

移動檔案偏移量:

lseek只將當前檔案偏移量記錄在核心中,不會引起任何I/O操作。

#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence);
//成功則返回新偏移量(從檔案頭開始計),出錯返回-1。off_t型別是長整型。

whence引數決定了offset引數是從何處開始計算偏移量:
SEEK_SET  //從檔案頭開始計,加上offset偏移量,offset為正
SEEK_CUR  //從當前位置開始計,加上offset偏移量,offset為正或負
SEEK_END  //從檔案尾開始計,加上offset偏移量,offset為正正或負

若是將偏移量移動到大於當前檔案的長度以後,再寫入資料,檔案中會形成一個空洞,檔案大小會變為新的長度,但是雖然檔案大小很大,其空洞部分卻並不會佔用額外的磁碟空間。

讀取:

#include <unistd.h>
ssize_t read(int filedes, void *buf, size_t nbytes);
//成功返回讀取位元組數,若已達到檔案結尾返回0, 出錯返回-1

void * 用於表示通用指標
ssize_t 為帶符號的整數
size_t 不帶符號整數

寫:

#include <unistd.h>
ssize_t write(int filedes, const void *buf, size_t nbytes);
//成功返回已寫位元組數,出錯返回-1

C語言中,任何需要多個函式呼叫的操作都不可能是原子操作,所以:

lseek(fd, 0L, SEEK_END);
write(fd, buf, 100);

上面這個追加檔案內容的方式並非原子操作,多執行緒情況下可能會存在問題(執行緒A將執行緒B在檔案尾部寫入的內容覆蓋掉)。
若要解決上面的問題,可以用open函式的O_APPEND標誌,此標誌會使核心每次對檔案進行寫之前,都會將程序的當前偏移量設定到檔案尾端,然後再寫,而且過程是原子的。

pread和pwrite:

#include <unistd.h>
ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset);
ssize_t pwrite(int filedes, const void *buf, size_t nbytes, off_t offset);
//返回值分別同read、write

pread相當於lseek和read, pwrite相當於lseek和write,但是有以下不同:
1、呼叫pread或pwrite時,無法中斷其定位和讀寫操作(原子的)。
2、pread或pwrite操作不會更新檔案偏移量。