[Linux C程式設計]檔案操作
檔案操作
1.什麼是系統呼叫?
所謂系統呼叫是指作業系統提供給使用者的一組“特殊”介面,使用者程式可以通過這組“特殊”介面來獲得作業系統核心提供的服務。
2.為什麼使用者程式不能直接訪問系統核心提供的服務呢?
由於在Linux中,為了更好地保護核心空間,將程式的執行空間分為核心空間和使用者空間(也就是常稱的核心態和使用者態),它們分別執行在不同的級別上,在邏輯上是相互隔離的。
因此,使用者程序在通常情況下不允許訪問核心資料,也無法使用核心函式,它們只能在使用者空間操作使用者資料,呼叫使用者空間的函式。
3.什麼是檔案?linux如何看待檔案?
Linux一點哲學,“一切皆為檔案”;在Linux中對目錄和裝置的操作都等同於對檔案的操作,都是使用檔案描述符來進行的。
Linux檔案可分為:普通檔案,目錄檔案,連結檔案,裝置檔案
4.什麼是檔案描述符?Linux核心如何分配描述符?
檔案描述符是一個非負的整數,它是一個索引值,並指向核心中每個程序開啟檔案的記錄表。
當開啟一個現存檔案或建立一個新檔案時,核心就向程序返回一個檔案描述符;當需要讀寫檔案時,也需要把檔案描述符作為引數傳遞給相應的函式。
一個程序啟動時,都會開啟3個檔案:標準輸入、標準輸出和標準出錯處理。
5.系統呼叫API
(1) creat
函式的作用: 建立一個檔案;
函式的原型: int creat(const char *pathname, mode_t mode);
filename:建立的檔名(包含路徑,預設為當前路徑)
mode:建立模式:S_IRUSR 可讀
S_IWUSR 可寫
S_IXUSR 可執行
S_IXRWU 可讀可寫可執行
(除用以上巨集來選擇建立模式,也可以用數字來表示,如0755)
檔案頭: #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
返回值:成功:新的檔案描述符;
出錯: -1
(2) open
函式的作用:開啟或建立檔案,在開啟或建立檔案時可以指定檔案的屬性及使用者的許可權等各種引數;
函式的原型:
int open(const char *pahtname, int flags);
int open(const char *pahtname, int flags, mode_t mode);
返回值:檔案描述符---成功;出錯:-1;
flags:
引數: O_RDONLY:只讀開啟
O_WRONLY:只寫開啟
O_RDWR:讀、寫開啟
O_CREAT:如果此檔案不存在則建立它,使用此選項時,需同時說明第三個引數
mode,用其說明該新檔案的存取許可權;
O_NONBLOCK:如果path name指的是一個塊特殊檔案或一個字元特殊檔案,則此選
擇項為此檔案的本次開啟操作和後續的I/O操作設定非阻塞方式。
O_APPEND:原來有內容,則會自動保留檔案內容,自動向下讀寫;
O_TRUNC: 檔案存在,有內容,檔案清空;
(3)read
函式的作用:從開啟的檔案中讀取count個位元組資料到buf所指向的緩衝區中。
函式的原型:ssize_t read(int fd, void *buf, size_t count);
包含的標頭檔案: #include <unistd.h>
返回值:正常是實際讀到的位元組數;
如果是在檔案結束或者是無資料,返回0;
出錯,-1;
(4)write
函式的作用: 把count個位元組從buf所指向的緩衝區中寫到檔案描述符fd所指向的檔案中
函式的原型: ssize_t write(int fd, const void *buf, size_t count);
標頭檔案: #include <unistd.h>
返回值: 成功會返回實際寫入的位元組數;
出錯:-1;
(5)lseek
函式的功能:將檔案讀寫指標相對whence移動offset個位元組。
函式的原型:int lseek(int fd, offset_t offset, int whence);
函式的引數:offset: 指標的微調,在指定的指標向前移動為負, 向後為正;
whence: SEEK_SET:放在檔案頭
SEEK_CUR:當前的位置;
SEEK_END: 檔案尾;
返回值:返回檔案當前指標到檔案開始的地方有多少位元組;
出錯-1;
6.標準庫函式
C庫函式的檔案操作是獨立於具體的作業系統平臺的。
7.什麼是不帶快取I/O操作?什麼是帶快取I/O操作?
不帶快取的I/O是對檔案描述符操作,帶快取的I/O是針對流的。
標準I/O庫就是帶快取的I/O,它由ANSI C標準說明。當然,標準I/O最終都會呼叫上面的I/O例程。
標準I/O庫代替使用者處理很多細節,比如快取分配、以優化長度執行I/O等。
標準I/O提供快取的目的就是減少呼叫read和write的次數,它對每個I/O流自動進行快取管理(標準I/O函式通常呼叫malloc來分配快取)。
它提供了三種類型的快取:
1) 全快取。當填滿標準I/O快取後才執行I/O操作。磁碟上的檔案通常是全快取的。
2) 行快取。當輸入輸出遇到新行符或快取滿時,才由標準I/O庫執行實際I/O操作。stdin、stdout通常是行快取的。
3) 無快取。相當於read、write了。stderr通常是無快取的,因為它必須儘快輸出。
一般而言,由系統選擇快取的長度,並自動分配。標準I/O庫在關閉流的時候自動釋放快取。
在標準I/O庫中,一個效率不高的不足之處是需要複製的資料量。
當使用每次一行函式fgets和fputs時,通常需要複製兩次資料:
第一次是在核心和標準I/O快取之間(當呼叫read和write時),
第二次是在標準I/O快取(通常系統分配和管理)和使用者程式中的行快取(fgets的引數就需要一個使用者行快取指標)之間。
8.庫函式
(1)fopen
函式的作用: 開啟檔案
函式的原型: FILE *fopen(const char *pth, const char *mode)
mode: r:讀,檔案必須存在;
r+:開啟可讀寫,檔案必須存在;
w:開啟只寫檔案,檔案不存在就會建立檔案; 檔案清0;
w+:開啟可讀寫的檔案,
a:附加的形式開啟只寫檔案,不存在就建立,存在就寫到原來的檔案尾。
a+:以附加的形式開啟可讀寫的檔案,不存在就建立,存在就寫到原來的文件尾。
b:二進位制檔案
標頭檔案: #include <stdio.h>
返回值: 成功是指向=檔案流的指標;
出錯返回NULL;
(2)fclose
函式的作用:關閉先前fopen()開啟的檔案,會將緩衝區內的資料寫入檔案中,並釋放系統所提供的檔案資源返回值。
函式的原型:int fclose(FILE * stream);
標頭檔案:#include<stdio.h>
返回值:若關動作成功則返回0
出錯返回EOF並把錯誤程式碼存到errno.
注:對檔案的讀和寫是最常用的檔案操作。在Linux C中提供了多種檔案讀寫的函式
字元讀寫函式:fgetc和fputc
字串讀寫函式:fgets和fputs
資料塊讀寫函式:fread和fwrite
格式化讀寫函式:fscanf和fprintf
(3)fputc
函式的作用: 將一個指定的字元寫入到檔案流中;
函式的原型: int fputc(int c, FILE *stream);
返回值: 返回寫入成功的字元,c; EOF則表示失敗。
(4)fgetc
函式的作用:從檔案流中讀取一個字元
函式原型: int fgetc(FILE *stream)
返回值:返回值正常的是讀取的字元;EOF表示到了檔案尾;
(5)fputs
函式的作用:將一個字串寫入到檔案內
函式的原型: int fputs(const char *s, FILE *stream)
返回值:成功返回寫成字元數; EOF表示出錯
(6)fgets
函式的作用:從檔案中讀取一個字串;
函式的原型:char *fgets(char *s, int size, FILE *steam)
函式的說明:fgets()用來從引數stream所指的檔案內讀入字元並存到引數s所指的記憶體空間中,知道出現換行字元、讀到檔案尾或是已讀了size-1個字元為止,最後會加上NULL作為字串結束。
返回值: 成功返回s, 出錯NULL。
(7)fread
函式的作用:從檔案流中讀取資料塊
函式原型:size_t fread(void *ptr, size_t size, size_t nmemb, FILE * stream);
函式引數:stream為已開啟的檔案指標
ptr:指向欲存放讀取進來的資料空間
讀取的字元數以引數nmemb來覺得
返回值:返回實際讀到資料塊的數目
比nmember小的話,可能是到了檔案尾,或者錯誤發生。
feof() :到檔案尾;
ferror():判斷錯誤的
(8) fwrite
函式的作用:將資料塊寫到檔案流中:
函式原型: size_t fwrite(const void * ptr, size_t size, size_t nmemb, FILE *stream);
函式引數:stream為已開啟的檔案指標,
ptr:指向欲寫入的資料地址,總共寫入的字元數以引數nmemb來決定
返回值: 實際寫入的nmemb數目;
(9)fprintf
函式的作用:格式化資料到檔案
函式的原型: int fprintf(FILE *stream, const char *format, ....);
返回值:成功返回實際輸入的字元數,失敗-1;
(10)fscanf
函式的作用: 格式化字串輸入
函式的原型: int fscanf(FILE *strem, const char *fromat,....)
返回值:成功返回引數數目,出錯-1
(11)fseek
函式的作用:移動檔案流的讀寫位置
函式的原型: int fseek(FILE * stream, long offset, int whence)
返回值:成功返回0, 出錯-1;
(12)ftell
函式的作用:讀取檔案流的讀寫位置;
函式的原型:long ftell(FILE * stream)
返回值: 成功返回當前的讀寫位置;
出錯-1;
(13)feof
函式的作用: 檢測檔案流是否到了檔案尾
函式的原型:int feof(FILE *steam)
返回值: 非零代表到了檔案尾,其他是0;
9.帶快取的I/O操作如何完成檔案的複製?
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#define BUFFER_SIZE 1024
int main(int argc,char *argv[])
{
int from_fd,to_fd;
int bytes_read, bytes_write;
char buffer[BUFFER_SIZE];
char *ptr;
if(argc!=3)
{
fprintf(stderr,"Usage:%s fromfile tofile/n/a",argv[0]);
exit(1);
}
if((from_fd=open(argv[1],O_RDONLY))==-1)
{
fprintf(stderr,"Open %s Error:%s/n",argv[1], strerror(errno));
exit(1);
}
if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)
{
fprintf(stderr,"Open %s Error:%s/n",argv[2],strerror(errno));
exit(1);
}
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))
{
if((bytes_read==-1)&&(errno!=EINTR)) /* 一個致命的錯誤發生了 */
break;
else if(bytes_read>0)
{
ptr=buffer;
while(bytes_write=write(to_fd,ptr,bytes_read))
{
if((bytes_write==-1)&&(errno!=EINTR)) /* 一個致命錯誤發生了 */
break;
else if(bytes_write==bytes_read) /* 寫完了所有讀的位元組 */
break;
/* 只寫了一部分,繼續寫 */
else if(bytes_write>0)
{
ptr+=bytes_write;
bytes_read-=bytes_write;
}
}
/* 寫的時候發生的致命錯誤 */
if(bytes_write==-1)
break;
}
}
close(from_fd);
close(to_fd);
exit(0);
}