文件操作
打開文件
1.使用open()函數(shù)打開和創(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:待打開文件的絕對(duì)路徑和文件名。
flags:打開的旗標(biāo)類型,或稱模式,
O_RDONLY 只讀模式打開文件
O_WRONLY 只寫模式打開文件
O_RDWR 讀寫模式打開文件
O_CREAT 若欲打開的文件不存在則自動(dòng)建立該文件
O_TRUNC 若文件存在并且以可寫的方式打開時(shí)震糖, 此旗標(biāo)會(huì)令文件長(zhǎng)度清為0,
而原來存于該文件的資料也會(huì)消失趴腋。
O_EXCL 如果O_CREAT 也被設(shè)置, 此指令會(huì)去檢查文件是否存在吊说。
文件若不存在則建立該文件,否則將導(dǎo)致打開文件錯(cuò)誤.
此外, 若O_CREAT 與O_EXCL 同時(shí)設(shè)置优炬,并且欲打開的文件為符號(hào)連接颁井,
則會(huì)打開文件失敗。
參數(shù)mode僅在flags中含有O_CREAT時(shí)有效蠢护,設(shè)定新建文文件的打開權(quán)限雅宾,有下列數(shù)種組合,
S_IRWXU 00700 權(quán)限,代表該文件所有者具有可讀葵硕、可寫及可執(zhí)行的權(quán)限眉抬。
S_IRUSR 或S_IREAD, 00400 權(quán)限,代表該文件所有者具有可讀取的權(quán)限懈凹。
S_IWUSR 或S_IWRITE, 00200 權(quán)限蜀变,代表該文件所有者具有可寫入的權(quán)限。
S_IXUSR 或S_IEXEC, 00100 權(quán)限介评,代表該文件所有者具有可執(zhí)行的權(quán)限库北。
S_IRWXG 00070 權(quán)限,代表該文件用戶組具有可讀们陆、可寫及可執(zhí)行的權(quán)限寒瓦。
S_IRGRP 00040 權(quán)限,代表該文件用戶組具有可讀的權(quán)限坪仇。
S_IWGRP 00020 權(quán)限杂腰,代表該文件用戶組具有可寫入的權(quán)限。
S_IXGRP 00010 權(quán)限椅文,代表該文件用戶組具有可執(zhí)行的權(quán)限喂很。
S_IRWXO 00007 權(quán)限际跪,代表其他用戶具有可讀衷笋、可寫及可執(zhí)行的權(quán)限庇麦。
S_IROTH 00004 權(quán)限憋活,代表其他用戶具有可讀的權(quán)限穴张。
S_IWOTH 00002 權(quán)限衍腥,代表其他用戶具有可寫入的權(quán)限蚪缀。
S_IXOTH 00001 權(quán)限疹启,xit代表其他用戶具有可執(zhí)行的權(quán)限林说。
函數(shù)返回值: 打開文件成功煎殷,返回一個(gè)文件描述符 >2;打開失敗腿箩,返回-1豪直。
提示:使用 access()作用戶認(rèn)證方面的判斷要特別小心, 例如在access()后再作open()空文件可能會(huì)造成系統(tǒng)安全上的問題珠移。
2.使用create()函數(shù)創(chuàng)建并打開文件
函數(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 待打開文件的絕對(duì)路徑和文件名暇韧。
mode 新創(chuàng)建文件的權(quán)限,見上面open()
函數(shù)返回值:若成功會(huì)返回新的文件描述符浓瞪,若有錯(cuò)誤發(fā)生則會(huì)返回-1懈玻。
提示:creat()無法建立特別的裝置文件,如果需要請(qǐng)使用mknod()乾颁。
讀寫文件
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ù)的來源湾盒。
buf 讀到的數(shù)據(jù)所存放的內(nèi)存空間的起始地址,同時(shí)文件的當(dāng)前讀寫位置向后移诅妹。
count 想要讀取的數(shù)據(jù)的字節(jié)數(shù)历涝,也是提供的存儲(chǔ)空間字節(jié)數(shù)。
函數(shù)說明及返回值: 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或是無可讀取的數(shù)據(jù)蚪战。
4.此外文件讀寫位置會(huì)隨讀取到的字節(jié)移動(dòng)牵现。
5.有錯(cuò)誤發(fā)生時(shí)則返回-1,而文件讀寫位置則無法預(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ù)向指定文件中寫入數(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 待寫入數(shù)據(jù)的文件的描述符
buf 寫入數(shù)據(jù)的起始地址
count 待寫入的數(shù)據(jù)的字節(jié)數(shù)
函數(shù)說明及返回值: write()會(huì)把參數(shù)buf 所指的內(nèi)存寫入count 個(gè)字節(jié)到參數(shù)fd 所指的文件內(nèi)墨坚。當(dāng)然,文件讀寫位置也會(huì)隨之移動(dòng)映挂。
如果順利會(huì)返回實(shí)際寫入數(shù)據(jù)的字節(jié)數(shù)泽篮,表示寫了部分或者全部的數(shù)據(jù)盗尸。
當(dāng)有錯(cuò)誤發(fā)生時(shí),返回-1帽撑,我們要根據(jù)錯(cuò)誤的類型來處理泼各。如果錯(cuò)誤為EINTR表示在寫時(shí)出現(xiàn)了中斷錯(cuò)誤。如果為EPIPE表示網(wǎng)絡(luò)連接出現(xiàn)了問題亏拉。
提示:對(duì)于普通文件扣蜻,寫操作始于 cfo 。如果打開文件時(shí)使用了 O_APPEND专筷,則每次寫操作都將數(shù)據(jù)寫入文件末尾。成功寫入后蒸苇,cfo 增加磷蛹,增量為實(shí)際寫入的字節(jié)數(shù)。
定位文件
預(yù)概念: 所有打開的文件都有一個(gè)當(dāng)前文件偏移量(current file offset)溪烤,以下簡(jiǎn)稱為 cfo味咳。cfo 通常是一個(gè)非負(fù)整數(shù),用于表明文件開始處到文件當(dāng)前位置的字節(jié)數(shù)檬嘀。讀寫操作通常開始于 cfo槽驶,并且使 cfo 增大,增量為讀寫的字節(jié)數(shù)鸳兽。文件被打開時(shí)掂铐,cfo 會(huì)被初始化為 0,除非使用了 O_APPEND 揍异。
使用lseek()函數(shù)定位指定已打開文件的讀寫指針
- 手冊(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 待重新定位讀寫指針位置的文件的描述符
offset 讀寫指針的偏移量(可正可負(fù)可為0)
whence 讀寫指針的偏移位置
SEEK_SET 相對(duì)文件首部偏移全陨,文件偏移量將被設(shè)置為 offset。
SEEK_CUR 相對(duì)文件當(dāng)前讀寫位置偏移衷掷,文件偏移量將被設(shè)置為 cfo 加上 offset辱姨,
offset 可以為正也可以為負(fù)。
SEEK_END 相對(duì)文件尾部偏移戚嗅,文件偏移量將被設(shè)置為文件長(zhǎng)度加上 offset雨涛,
offset 可以為正也可以為負(fù)。
函數(shù)說明及返回值: 每一個(gè)已打開的文件都有一個(gè)讀寫位置懦胞,當(dāng)打開文件時(shí)通常其讀寫位置是指向文件開頭替久,若是以附加的方式打開文件(如O_APPEND),則讀寫位置會(huì)指向文件尾躏尉。當(dāng)read()或write()時(shí)侣肄,讀寫位置會(huì)隨之增加,lseek()便是用來控制該文件的讀寫位置醇份。參數(shù)fildes 為已打開的文件描述詞稼锅,參數(shù)offset 為根據(jù)參數(shù)whence來移動(dòng)讀寫位置的位移數(shù)吼具。當(dāng)調(diào)用成功時(shí)則返回目前的讀寫位置,也就是距離文件多少個(gè)字節(jié)數(shù)矩距。若有錯(cuò)誤則返回-1拗盒。
例:
將讀寫位置移到文件開頭時(shí): lseek(int fildes, 0, SEEK_SET);
將讀寫位置移到文件尾時(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 來判斷 lseek 成功與否潮剪,而應(yīng)該測(cè)試 lseek 的返回值是否等于 -1 來判斷 lseek 成功與否涣楷。
3.lseek 僅將 cfo 保存于內(nèi)核中,不會(huì)導(dǎo)致任何 I/O 操作抗碰。這個(gè) cfo 將被用于之后的讀寫操作狮斗。
4.如果 offset 比文件的當(dāng)前長(zhǎng)度更大,下一個(gè)寫操作就會(huì)把文件“撐大(extend)”弧蝇。這就是所謂的在文件里創(chuàng)造"空洞(hole)”碳褒。沒有被實(shí)際寫入文件的所有字節(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()打開的文件描述符。
函數(shù)說明及返回值: 當(dāng)使用完已打開的文件后若已不再需要?jiǎng)t可使用 close()關(guān)閉該文件, 而close()會(huì)讓數(shù)據(jù)寫回磁盤, 并釋放該文件所占用的資源. 參數(shù)fd 為先前由open()或creat()所返回的文件描述詞.**返回值:若文件順利關(guān)閉則返回0, 發(fā)生錯(cuò)誤時(shí)返回-1.
提示:雖然在進(jìn)程結(jié)束時(shí)鹃觉,系統(tǒng)會(huì)自動(dòng)關(guān)閉已打開的文件专酗,但仍建議自行關(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 以只讀方式打開源文件
if((src_fd = open(src_file, O_RDONLY)) == -1)
{
perror("open src error");
return 1;
}
// 1.2 以只寫方式打開目的文件
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ù)寫入到目的文件中
// 直到讀到源文件的尾部為止
// 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.打開源文件
// 2.判斷目的文件是否存在
// 3.如果目的文件存在,提示是否覆蓋
// 4.如果選擇覆蓋斑鼻,則以只寫的方式打開文件蒋纬,并截短文件內(nèi)容(O_TRUNC)
// 5.如果選擇不覆蓋,則提醒輸入新的保存文件名,并已只寫方式打開
// 6.循環(huán)讀取源文件內(nèi)容蜀备,寫入到目的文件中
// 7.關(guān)閉已打開的文件
// 思考題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)用