原帖見:
http://fanqiang.chinaunix.net/a4/b2/20010508/113315.html
前言:
我們?cè)谶@一節(jié)將要討論linux下文件操作的各個(gè)函數(shù).
1.文件的創(chuàng)建和讀寫
2.文件的各個(gè)屬性
3.目錄文件的操作
4.管道文件
--------------------------------------------------------------------------------
1。文件的創(chuàng)建和讀寫
我假設(shè)你已經(jīng)知道了標(biāo)準(zhǔn)級(jí)的文件操作的各個(gè)函數(shù)(fopen,fread,fwrite等等).當(dāng)然如果你不清楚的話也不要著急.我們討論的系統(tǒng)級(jí)的文件操作實(shí)際上是為標(biāo)準(zhǔn)級(jí)文件操作服務(wù)的.
當(dāng)我們需要打開一個(gè)文件進(jìn)行讀寫操作的時(shí)候,我們可以使用系統(tǒng)調(diào)用函數(shù)open.使用完成以后我們調(diào)用另外一個(gè)close函數(shù)進(jìn)行關(guān)閉操作.
#include
#include
#include
#include
int open(const char *pathname,int flags);
int open(const char *pathname,int flags,mode_t mode);
int close(int fd);
open函數(shù)有兩個(gè)形式.其中pathname是我們要打開的文件名(包含路徑名稱,缺省是認(rèn)為在當(dāng)前路徑下面).flags可以去下面的一個(gè)值或者是幾個(gè)值的組合.
O_RDONLY:以只讀的方式打開文件.
O_WRONLY:以只寫的方式打開文件.
O_RDWR:以讀寫的方式打開文件.
O_APPEND:以追加的方式打開文件.
O_CREAT:創(chuàng)建一個(gè)文件.
O_EXEC:如果使用了O_CREAT而且文件已經(jīng)存在,就會(huì)發(fā)生一個(gè)錯(cuò)誤.
O_NOBLOCK:以非阻塞的方式打開一個(gè)文件.
O_TRUNC:如果文件已經(jīng)存在,則刪除文件的內(nèi)容.
前面三個(gè)標(biāo)志只能使用任意的一個(gè).如果使用了O_CREATE標(biāo)志,那么我們要使用open的第二種形式.還要指定mode標(biāo)志,用來表示文件的訪問權(quán)限.mode可以是以下情況的組合.
-----------------------------------------------------------------
S_IRUSR 用戶可以讀 S_IWUSR 用戶可以寫
S_IXUSR 用戶可以執(zhí)行 S_IRWXU 用戶可以讀寫執(zhí)行
-----------------------------------------------------------------
S_IRGRP 組可以讀 S_IWGRP 組可以寫
S_IXGRP 組可以執(zhí)行 S_IRWXG 組可以讀寫執(zhí)行
-----------------------------------------------------------------
S_IROTH 其他人可以讀 S_IWOTH 其他人可以寫
S_IXOTH 其他人可以執(zhí)行 S_IRWXO 其他人可以讀寫執(zhí)行
-----------------------------------------------------------------
S_ISUID 設(shè)置用戶執(zhí)行ID S_ISGID 設(shè)置組的執(zhí)行ID
-----------------------------------------------------------------
我們也可以用數(shù)字來代表各個(gè)位的標(biāo)志.Linux總共用5個(gè)數(shù)字來表示文件的各種權(quán)限.
00000.第一位表示設(shè)置用戶ID.第二位表示設(shè)置組ID,第三位表示用戶自己的權(quán)限位,第四位表示組的權(quán)限,最后一位表示其他人的權(quán)限.
每個(gè)數(shù)字可以取1(執(zhí)行權(quán)限),2(寫權(quán)限),4(讀權(quán)限),0(什么也沒有)或者是這幾個(gè)值的和.
比如我們要?jiǎng)?chuàng)建一個(gè)用戶讀寫執(zhí)行,組沒有權(quán)限,其他人讀執(zhí)行的文件.設(shè)置用戶ID位那么我們可以使用的模式是--1(設(shè)置用戶ID)0(組沒有設(shè)置)7(1+2+4)0(沒有權(quán)限,使用缺省)5(1+4)即10705:
open("temp",O_CREAT,10705);
如果我們打開文件成功,open會(huì)返回一個(gè)文件描述符.我們以后對(duì)文件的所有操作就可以對(duì)這個(gè)文件描述符進(jìn)行操作了.
當(dāng)我們操作完成以后,我們要關(guān)閉文件了,只要調(diào)用close就可以了,其中fd是我們要關(guān)閉的文件描述符.
文件打開了以后,我們就要對(duì)文件進(jìn)行讀寫了.我們可以調(diào)用函數(shù)read和write進(jìn)行文件的讀寫.
#include
ssize_t read(int fd, void *buffer,size_t count);
ssize_t write(int fd, const void *buffer,size_t count);
fd是我們要進(jìn)行讀寫操作的文件描述符,buffer是我們要寫入文件內(nèi)容或讀出文件內(nèi)容的內(nèi)存地址.count是我們要讀寫的字節(jié)數(shù).
對(duì)于普通的文件read從指定的文件(fd)中讀取count字節(jié)到buffer緩沖區(qū)中(記住我們必須提供一個(gè)足夠大的緩沖區(qū)),同時(shí)返回count.
如果read讀到了文件的結(jié)尾或者被一個(gè)信號(hào)所中斷,返回值會(huì)小于count.如果是由信號(hào)中斷引起返回,而且沒有返回?cái)?shù)據(jù),read會(huì)返回-1,且設(shè)置errno為EINTR.當(dāng)程序讀到了文件結(jié)尾的時(shí)候,read會(huì)返回0.
write從buffer中寫count字節(jié)到文件fd中,成功時(shí)返回實(shí)際所寫的字節(jié)數(shù).
下面我們學(xué)習(xí)一個(gè)實(shí)例,這個(gè)實(shí)例用來拷貝文件.
#include
#include
#include
#include
#include
#include
#include
#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);
}
/* 創(chuàng)建目的文件 */
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);
}
/* 以下代碼是一個(gè)經(jīng)典的拷貝文件的代碼 */
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))
{
/* 一個(gè)致命的錯(cuò)誤發(fā)生了 */
if((bytes_read==-1)&&(errno!=EINTR)) break;
else if(bytes_read>0)
{
ptr=buffer;
while(bytes_write=write(to_fd,ptr,bytes_read))
{
/* 一個(gè)致命錯(cuò)誤發(fā)生了 */
if((bytes_write==-1)&&(errno!=EINTR))break;
/* 寫完了所有讀的字節(jié) */
else if(bytes_write==bytes_read) break;
/* 只寫了一部分,繼續(xù)寫 */
else if(bytes_write>0)
{
ptr+=bytes_write;
bytes_read-=bytes_write;
}
}
/* 寫的時(shí)候發(fā)生的致命錯(cuò)誤 */
if(bytes_write==-1)break;
}
}
close(from_fd);
close(to_fd);
exit(0);
}
2。文件的各個(gè)屬性
文件具有各種各樣的屬性,除了我們上面所知道的文件權(quán)限以外,文件還有創(chuàng)建時(shí)間,大小等等屬性.
有時(shí)侯我們要判斷文件是否可以進(jìn)行某種操作(讀,寫等等).這個(gè)時(shí)候我們可以使用access函數(shù).
#include
int access(const char *pathname,int mode);
pathname:是文件名稱,mode是我們要判斷的屬性.可以取以下值或者是他們的組合.
R_OK文件可以讀,W_OK文件可以寫,X_OK文件可以執(zhí)行,F_OK文件存在.當(dāng)我們測(cè)試成功時(shí),函數(shù)返回0,否則如果有一個(gè)條件不符時(shí),返回-1.
如果我們要獲得文件的其他屬性,我們可以使用函數(shù)stat或者fstat.
#include
#include
int stat(const char *file_name,struct stat *buf);
int fstat(int filedes,struct stat *buf);
struct stat {
dev_t st_dev; /* 設(shè)備 */
ino_t st_ino; /* 節(jié)點(diǎn) */
mode_t st_mode; /* 模式 */
nlink_t st_nlink; /* 硬連接 */
uid_t st_uid; /* 用戶ID */
gid_t st_gid; /* 組ID */
dev_t st_rdev; /* 設(shè)備類型 */
off_t st_off; /* 文件字節(jié)數(shù) */
unsigned long st_blksize; /* 塊大小 */
unsigned long st_blocks; /* 塊數(shù) */
time_t st_atime; /* 最后一次訪問時(shí)間 */
time_t st_mtime; /* 最后一次修改時(shí)間 */
time_t st_ctime; /* 最后一次改變時(shí)間(指屬性) */
};
stat用來判斷沒有打開的文件,而fstat用來判斷打開的文件.我們使用最多的屬性是st_mode.通過著屬性我們可以判斷給定的文件是一個(gè)普通文件還是一個(gè)目錄,連接等等.可以使用下面幾個(gè)宏來判斷.
S_ISLNK(st_mode):是否是一個(gè)連接.S_ISREG是否是一個(gè)常規(guī)文件.S_ISDIR是否是一個(gè)目錄S_ISCHR是否是一個(gè)字符設(shè)備.S_ISBLK是否是一個(gè)塊設(shè)備S_ISFIFO是否 是一個(gè)FIFO文件.S_ISSOCK是否是一個(gè)SOCKET文件. 我們會(huì)在下面說明如何使用這幾個(gè)宏的.
3。目錄文件的操作
在我們編寫程序的時(shí)候,有時(shí)候會(huì)要得到我們當(dāng)前的工作路徑。C庫函數(shù)提供了getcwd來解決這個(gè)問題。
#include
char *getcwd(char *buffer,size_t size);
我們提供一個(gè)size大小的buffer,getcwd會(huì)把我們當(dāng)前的路徑考到buffer中.如果buffer太小,函數(shù)會(huì)返回-1和一個(gè)錯(cuò)誤號(hào).
Linux提供了大量的目錄操作函數(shù),我們學(xué)習(xí)幾個(gè)比較簡單和常用的函數(shù).
#include
#include
#include
#include
#include
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很容易就是我們創(chuàng)建一個(gè)目錄,opendir打開一個(gè)目錄為以后讀做準(zhǔn)備.readdir讀一個(gè)打開的目錄.rewinddir是用來重讀目錄的和我們學(xué)的rewind函數(shù)一樣.closedir是關(guān)閉一個(gè)目錄.telldir和seekdir類似與ftee和fseek函數(shù).
下面我們開發(fā)一個(gè)小程序,這個(gè)程序有一個(gè)參數(shù).如果這個(gè)參數(shù)是一個(gè)文件名,我們輸出這個(gè)文件的大小和最后修改的時(shí)間,如果是一個(gè)目錄我們輸出這個(gè)目錄下所有文件的大小和修改時(shí)間.
#include
#include
#include
#include
#include
#include
#include
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-
closedir(dirp);
exit(1);
}
4。管道文件
Linux提供了許多的過濾和重定向程序,比如more cat
等等.還提供了< > | <<等等重定向操作符.在這些過濾和重 定向程序當(dāng)中,都用到了管道這種特殊的文件.系統(tǒng)調(diào)用pipe可以創(chuàng)建一個(gè)管道.
#include
int pipe(int fildes[2]);
pipe調(diào)用可以創(chuàng)建一個(gè)管道(通信緩沖區(qū)).當(dāng)調(diào)用成功時(shí),我們可以訪問文件描述符fildes[0],fildes[1].其中fildes[0]是用來讀的文件描述符,而fildes[1]是用來寫的文件描述符.
在實(shí)際使用中我們是通過創(chuàng)建一個(gè)子進(jìn)程,然后一個(gè)進(jìn)程寫,一個(gè)進(jìn)程讀來使用的.
關(guān)于進(jìn)程通信的詳細(xì)情況請(qǐng)查看進(jìn)程通信
#include
#include
#include
#include
#include
#include
#include
#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,'\0',BUFFER+1);
read(fd[0],buffer,BUFFER);
printf("Parent[%d] Read:%s\n",getpid(),buffer);
exit(1);
}
}
為了實(shí)現(xiàn)重定向操作,我們需要調(diào)用另外一個(gè)函數(shù)dup2.
#include
int dup2(int oldfd,int newfd);
dup2將用oldfd文件描述符來代替newfd文件描述符,同時(shí)關(guān)閉newfd文件描述符.也就是說,
所有向newfd操作都轉(zhuǎn)到oldfd上面.下面我們學(xué)習(xí)一個(gè)例子,這個(gè)例子將標(biāo)準(zhǔn)輸出重定向到一個(gè)文件.
#include
#include
#include
#include
#include
#include
#include
#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);
}
好了,文件一章我們就暫時(shí)先討論到這里,學(xué)習(xí)好了文件的操作我們其實(shí)已經(jīng)可以寫出一些比較有用的程序了.我們可以編寫一個(gè)實(shí)現(xiàn)例如dir,mkdir,cp,mv等等常用的文件操作命令了.
想不想自己寫幾個(gè)試一試呢?
附:常見的頭文件是:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
posted on 2010-03-02 10:11
deercoder 閱讀(420)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
Unix/Linux