3)Linux程式設計入門–檔案操作

來源:互聯網
上載者:User

3)Linux程式設計入門--檔案操作
Linux下檔案的操作
前言:
我們在這一節將要討論linux下檔案操作的各個函數.
檔案的建立和讀寫
檔案的各個屬性
目錄檔案的操作
管道檔案
------------------------------------------------------------------------
1。檔案的建立和讀寫
我假設你已經知道了標準級的檔案操作的各個函數(fopen,fread,fwrite等等).當然
如果你不清楚的話也不要著急.我們討論的系統級的檔案操作實際上是為標準級檔案操作
服務的.
當我們需要開啟一個檔案進行讀寫操作的時候,我們可以使用系統調用函數open.使用完
成以後我們調用另外一個close函數進行關閉操作.
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
int close(int fd);
open函數有兩個形式.其中pathname是我們要開啟的檔案名稱(包含路徑名稱,預設是認為在
當前路徑下面).flags可以去下面的一個值或者是幾個值的組合.
O_RDONLY:以唯讀方式開啟檔案.
O_WRONLY:以唯寫的方式開啟檔案.
O_RDWR:以讀寫的方式開啟檔案.
O_APPEND:以追加的方式開啟檔案.
O_CREAT:建立一個檔案.
O_EXEC:如果使用了O_CREAT而且檔案已經存在,就會發生一個錯誤.
O_NOBLOCK:以非阻塞的方式開啟一個檔案.
O_TRUNC:如果檔案已經存在,則刪除檔案的內容.
前面三個標誌只能使用任意的一個.如果使用了O_CREATE標誌,那麼我們要使用open的第
二種形式.還要指定mode標誌,用來表示檔案的存取權限.mode可以是以下情況的組合.
-----------------------------------------------------------------
S_IRUSR 使用者可以讀 S_IWUSR 使用者可以寫
S_IXUSR 使用者可以執行 S_IRWXU 使用者可以讀寫執行
-----------------------------------------------------------------
S_IRGRP 組可以讀 S_IWGRP 組可以寫
S_IXGRP 組可以執行 S_IRWXG 組可以讀寫執行
-----------------------------------------------------------------
S_IROTH 其他人可以讀 S_IWOTH 其他人可以寫
S_IXOTH 其他人可以執行 S_IRWXO 其他人可以讀寫執行
-----------------------------------------------------------------
S_ISUID 設定使用者執行ID S_ISGID 設定組的執行ID
-----------------------------------------------------------------
我們也可以用數字來代表各個位的標誌.Linux總共用5個數字來表示檔案的各種許可權.
00000.第一位表示設定使用者ID.第二位表示設定組ID,第三位表示使用者自己的許可權位,第四
位表示組的許可權,最後一位表示其他人的許可權.
每個數字可以取1(執行許可權),2(寫入權限),4(讀許可權),0(什麼也沒有)或者是這幾個值的和
..
比如我們要建立一個使用者讀寫執行,組沒有許可權,其他人讀執行的檔案.設定使用者ID位那麼
我們可以使用的模式是--1(設定使用者ID)0(組沒有設定)7(1+2+4)0(沒有許可權,使用預設)
5(1+4)即10705:
open("temp",O_CREAT,10705);
如果我們開啟檔案成功,open會返回一個檔案描述符.我們以後對檔案的所有操作就可以
對這個檔案描述符進行操作了.
當我們操作完成以後,我們要關閉檔案了,只要調用close就可以了,其中fd是我們要關閉
的檔案描述符.
檔案開啟了以後,我們就要對檔案進行讀寫了.我們可以調用函數read和write進行檔案的
讀寫.
#include <unistd.h>
ssize_t read(int fd, void *buffer,size_t count);
ssize_t write(int fd, const void *buffer,size_t count);
fd是我們要進行讀寫操作的檔案描述符,buffer是我們要寫入檔案內容或讀出檔案內容的
記憶體位址.count是我們要讀寫的位元組數.
對於普通的檔案read從指定的檔案(fd)中讀取count位元組到buffer緩衝區中(記住我們必
須提供一個足夠大的緩衝區),同時返回count.
如果read讀到了檔案的結尾或者被一個訊號所中斷,傳回值會小於count.如果是由訊號中
斷引起返回,而且沒有返回資料,read會返回-1,且設定errno為EINTR.當程式讀到了檔案
結尾的時候,read會返回0.
write從buffer中寫count位元組到檔案fd中,成功時返回實際所寫的位元組數.
下面我們學習一個執行個體,這個執行個體用來拷貝檔案.
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.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);
}
2。檔案的各個屬性
檔案具有各種各樣的屬性,除了我們上面所知道的檔案許可權以外,檔案還有建立時間
,大小等等屬性.
有時侯我們要判斷檔案是否可以進行某種操作(讀,寫等等).這個時候我們可以使用acce
ss函數.
#include <unistd.h>

int access(const char *pathname,int mode);
pathname:是檔案名稱,mode是我們要判斷的屬性.可以取以下值或者是他們的組合.
R_OK檔案可以讀,W_OK檔案可以寫,X_OK檔案可以執行,F_OK檔案存在.當我們測試成功時
,函數返回0,否則如果有一個條件不符時,返回-1.
如果我們要獲得檔案的其他屬性,我們可以使用函數stat或者fstat.
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *file_name,struct stat *buf);
int fstat(int filedes,struct stat *buf);
struct stat {
dev_t st_dev; /* 裝置 */
ino_t st_ino; /* 節點 */
mode_t st_mode; /* 模式 */
nlink_t st_nlink; /* 硬串連 */
uid_t st_uid; /* 使用者ID */
gid_t st_gid; /* 組ID */
dev_t st_rdev; /* 裝置類型 */
off_t st_off; /* 檔案位元組數 */
unsigned long st_blksize; /* 塊大小 */
unsigned long st_blocks; /* 塊數 */
time_t st_atime; /* 最後一次訪問時間 */
time_t st_mtime; /* 最後一次修改時間 */
time_t st_ctime; /* 最後一次改變時間(指屬性) */
};
stat用來判斷沒有開啟的檔案,而fstat用來判斷開啟的檔案.我們使用最多的屬性是st_
mode.通過著屬性我們可以判斷給定的檔案是一個普通檔案還是一個目錄,串連等等.可以
使用下面幾個宏來判斷.
S_ISLNK(st_mode):是否是一個串連.S_ISREG是否是一個常規檔案.S_ISDIR是否是一個目
錄S_ISCHR是否是一個字元裝置.S_ISBLK是否是一個塊裝置S_ISFIFO是否 是一個FIFO文
件.S_ISSOCK是否是一個SOCKET檔案. 我們會在下面說明如何使用這幾個宏的.
3。目錄檔案的操作
在我們編寫程式的時候,有時候會要得到我們當前的工作路徑。C庫函數提供了get
cwd來解決這個問題。
#include <unistd.h>

char *getcwd(char *buffer,size_t size);
我們提供一個size大小的buffer,getcwd會把我們當前的路徑考到buffer中.如果buffer
太小,函數會返回-1和一個錯誤號碼.
Linux提供了大量的目錄操作函數,我們學習幾個比較簡單和常用的函數.
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int mkdir(const char *path,mode_t mode);
DIR *opendir(const char *path);
struct dirent *readdir(DIR *dir);
void rewinddir(DIR *dir);
off_t telldir(DIR *dir);
void seekdir(DIR *dir,off_t off);
int closedir(DIR *dir);
struct dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[NAME_MAX+1]; /* 檔案名稱 */
mkdir很容易就是我們建立一個目錄,opendir開啟一個目錄為以後讀做準備.readdir讀一
個開啟的目錄.rewinddir是用來重讀目錄的和我們學的rewind函數一樣.closedir是關閉
一個目錄.telldir和seekdir類似與ftee和fseek函數.
下面我們開發一個小程式,這個程式有一個參數.如果這個參數是一個檔案名稱,我們輸出這
個檔案的大小和最後修改的時間,如果是一個目錄我們輸出這個目錄下所有檔案的大小和
修改時間.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
static int get_file_size_time(const char *filename)
{
struct stat statbuf;
if(stat(filename,&statbuf)==-1)
{
printf("Get stat on %s Error:%s/n",
filename,strerror(errno));
return(-1);
}
if(S_ISDIR(statbuf.st_mode))return(1);
if(S_ISREG(statbuf.st_mode))
printf("%s size:%ld bytes/tmodified at %s",
filename,statbuf.st_size,ctime(&statbuf.st_mtime));

return(0);
}
int main(int argc,char **argv)
{
DIR *dirp;
struct dirent *direntp;
int stats;
if(argc!=2)
{
printf("Usage:%s filename/n/a",argv[0]);
exit(1);
}
if(((stats=get_file_size_time(argv[1]))==0)||(stats==-1))exit(1);
if((dirp=opendir(argv[1]))==NULL)
{
printf("Open Directory %s Error:%s/n",
argv[1],strerror(errno));
exit(1);
}
while((direntp=readdir(dirp))!=NULL)
if(get_file_size_time(direntp-<d_name)==-1)break;
closedir(dirp);
exit(1);
}
4。管道檔案
Linux提供了許多的過濾和重新導向程式,比如more cat
等等.還提供了< > | <<等等重新導向操作符.在這些過濾和重 定向程式當中,都用到了管
道這種特殊的檔案.系統調用pipe可以建立一個管道.
#include<unistd.h>

int pipe(int fildes[2]);
pipe調用可以建立一個管道(通訊緩衝區).當調用成功時,我們可以訪問檔案描述符fild
es[0],fildes[1].其中fildes[0]是用來讀的檔案描述符,而fildes[1]是用來寫的檔案描
述符.
在實際使用中我們是通過建立一個子進程,然後一個進程寫,一個進程讀來使用的.
關於進程通訊的詳細情況請查看進程通訊
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER 255
int main(int argc,char **argv)
{
char buffer[BUFFER+1];
int fd[2];
if(argc!=2)
{
fprintf(stderr,"Usage:%s string/n/a",argv[0]);
exit(1);
}
if(pipe(fd)!=0)
{
fprintf(stderr,"Pipe Error:%s/n/a",strerror(errno));
exit(1);
}
if(fork()==0)
{
close(fd[0]);
printf("Child[%d] Write to pipe/n/a",getpid());
snprintf(buffer,BUFFER,"%s",argv[1]);
write(fd[1],buffer,strlen(buffer));
printf("Child[%d] Quit/n/a",getpid());
exit(0);
}
else
{
close(fd[1]);
printf("Parent[%d] Read from pipe/n/a",getpid());
memset(buffer,'',BUFFER+1);
read(fd[0],buffer,BUFFER);
printf("Parent[%d] Read:%s/n",getpid(),buffer);
exit(1);
}
}
為了實現重新導向操作,我們需要調用另外一個函數dup2.
#include <unistd.h>

int dup2(int oldfd,int newfd);
dup2將用oldfd檔案描述符來代替newfd檔案描述符,同時關閉newfd檔案描述符.也就是說
,
所有向newfd操作都轉到oldfd上面.下面我們學習一個例子,這個例子將標準輸出重新導向
到一個檔案.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
int fd;
char buffer[BUFFER_SIZE];
if(argc!=2)
{
fprintf(stderr,"Usage:%s outfilename/n/a",argv[0]);
exit(1);
}
if((fd=open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR))==-1)
{
fprintf(stderr,"Open %s Error:%s/n/a",argv[1],strerror(errno));
exit(1);
}
if(dup2(fd,STDOUT_FILENO)==-1)
{
fprintf(stderr,"Redirect Standard Out Error:%s/n/a",strerror(errno));
exit(1);
}
fprintf(stderr,"Now,please input string");
fprintf(stderr,"(To quit use CTRL+D)/n");
while(1)
{
fgets(buffer,BUFFER_SIZE,stdin);
if(feof(stdin))break;
write(STDOUT_FILENO,buffer,strlen(buffer));
}
exit(0);
}
好了,檔案一章我們就暫時先討論到這裡,學習好了檔案的操作我們其實已經可以寫出一
些比較有用的程式了.我們可以編寫一個實現例如dir,mkdir,cp,mv等等常用的檔案操作
命令了.
想不想自己寫幾個試一試呢?

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.