Java实现opendir的api_linux文件操作API
from http://blog.chinaunix.net/uid-21411227-id-1826945.html
2010.11
1.Linux文件系統Linux支持多種文件系統,如ext、ext2、minix、iso9660、msdos、fat、vfat、nfs等。在這些具體文件系統的上層,Linux提供了虛擬文件系統(VFS)來統一它們的行為,虛擬文件系統為不同的文件系統與內核的通信提供了一致的接口。
在Linux平臺下對文件編程可以使用兩類函數:(1)Linux操作系統文件API;(2)C語言I/O庫函數。 前者依賴于Linux系統調用,后者實際上與操作系統是獨立的,因為在任何操作系統下,使用C語言I/O庫函數操作文件的方法都是相同的。
2.Linux文件API
Linux的文件操作API涉及到創建、打開、讀寫和關閉文件。
創建
int creat(const char *filename, mode_t mode);
參數mode指定新建文件的存取權限,它同umask一起決定文件的最終權限(mode&umask),其中umask代表了文件在創建時需要去掉的一些存取權限。umask可通過系統調用umask()來改變:
int umask(int newmask);
該調用將umask設置為newmask,然后返回舊的umask,它只影響讀、寫和執行權限。
打開
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
open函數有兩個形式,其中pathname是我們要打開的文件名(包含路徑名稱,缺省是認為在當前路徑下面),flags可以是下面的一個值或者是幾個值的組合:
標志
含義
O_RDONLY
以只讀的方式打開文件
O_WRONLY
以只寫的方式打開文件
O_RDWR
以讀寫的方式打開文件
O_APPEND
以追加的方式打開文件
O_CREAT
創建一個文件
O_EXEC
如果使用了O_CREAT而且文件已經存在,
就會發生一個錯誤
O_NOBLOCK
以非阻塞的方式打開一個文件
O_TRUNC
如果文件已經存在,則刪除文件的內容
O_RDONLY、O_WRONLY、O_RDWR三個標志只能使用任意的一個。如果使用了O_CREATE標志,則使用的函數是int open(const char *pathname,int flags,mode_t mode);這個時候我們還要指定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個數字來表示文件的各種權限:第一位表示設置用戶ID;第二位表示設置組ID;第三位表示用戶自己的權限位;第四位表示組的權限;最后一位表示其他人的權限。每個數字可以取1(執行權限)、2(寫權限)、4(讀權限)、0(無)或者是這些值的和。例如,要創建一個用戶可讀、可寫、可執行,但是組沒有權限,其他人可以讀、可以執行的文件,并設置用戶ID位。那么,我們應該使用的模式是1(設置用戶ID)、0(不設置組ID)、7(1+2+4,讀、寫、執行)、0(沒有權限)、5(1+4,讀、執行)即10705:
open("test", O_CREAT, 10705);
上述語句等價于:
open("test", O_CREAT, S_IRWXU | S_IROTH | S_IXOTH | S_ISUID );
如果文件打開成功,open函數會返回一個文件描述符,以后對該文件的所有操作就可以通過對這個文件描述符進行操作來實現。讀寫
在文件打開以后,我們才可對文件進行讀寫了,Linux中提供文件讀寫的系統調用是read、write函數:
int read(int fd, const void *buf, size_t length);
int write(int fd, const void *buf, size_t length);
其中參數buf為指向緩沖區的指針,length為緩沖區的大小(以字節為單位)。函數read()實現從文件描述符fd所指定的文件中讀取length個字節到buf所指向的緩沖區中,返回值為實際讀取的字節數。函數write實現將把length個字節從buf指向的緩沖區中寫到文件描述符fd所指向的文件中,返回值為實際寫入的字節數。 以O_CREAT為標志的open實際上實現了文件創建的功能,因此,下面的函數等同creat()函數:
int open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
定位
對于隨機文件,我們可以隨機的指定位置讀寫,使用如下函數進行定位:
int lseek(int fd, offset_t offset, int whence);
lseek()將文件讀寫指針相對whence移動offset個字節。操作成功時,返回文件指針相對于文件頭的位置。參數whence可使用下述值: SEEK_SET:相對文件開頭 SEEK_CUR:相對文件讀寫指針的當前位置 SEEK_END:相對文件末尾 offset可取負值,例如下述調用可將文件指針相對當前位置向前移動5個字節:
lseek(fd, -5, SEEK_CUR);
由于lseek函數的返回值為文件指針相對于文件頭的位置,因此下列調用的返回值就是文件的長度:
lseek(fd, 0, SEEK_END);
關閉
當我們操作完成以后,我們要關閉文件了,只要調用close就可以了,其中fd是我們要關閉的文件描述符:
int close(int fd);
下面我們學習一個實例,這個實例用來拷貝文件.
#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);
}
/*創建目的文件*/
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);
}
3:文件的各個屬性
文件具有各種各樣的屬性,除了我們上面所知道的文件權限以外,文件還有創建時間,大小等等屬性.
有時侯我們要判斷文件是否可以進行某種操作(讀,寫等等).這個時候我們可以使用access函數.
#include
int access(const char *pathname,int mode);
pathname:是文件名稱,mode是我們要判斷的屬性.可以取以下值或者是他們的組合.
R_OK文件可以讀,W_OK文件可以寫,X_OK文件可以執行,F_OK文件存在.當我們測試成功時,函數返回0,否則如果有一個條件不符時,返回-1.
如果我們要獲得文件的其他屬性,我們可以使用函數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; /*設備*/
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文件.
4:目錄文件的操作
在我們編寫程序的時候,有時候會要得到我們當前的工作路徑。C庫函數提供了getcwd來解決這個問題。
#include
char *getcwd(char *buffer,size_t size);
我們提供一個size大小的buffer,getcwd會把我們當前的路徑考到buffer中.如果buffer太小,函數會返回-1和一個錯誤號.
Linux提供了大量的目錄操作函數,我們學習幾個比較簡單和常用的函數.
#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很容易就是我們創建一個目錄,opendir打開一個目錄為以后讀做準備.readdir讀一個打開的目錄.rewinddir是用來重讀目錄的和我們學的rewind函數一樣.closedir是關閉一個目錄.telldir和seekdir類似與ftee和fseek函數.
下面我們開發一個小程序,這個程序有一個參數.如果這個參數是一個文件名,我們輸出這個文件的大小和最后修改的時間,如果是一個目錄我們輸出這個目錄下所有文件的大小和修改時間.
#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-br> closedir(dirp);
exit(1);
}
5:管道文件
Linux提供了許多的過濾和重定向程序,比如more cat
等等.還提供了< > | <
#include
int pipe(int fildes[2]);
pipe調用可以創建一個管道(通信緩沖區).當調用成功時,我們可以訪問文件描述符fildes[0],fildes[1].其中fildes[0]是用來讀的文件描述符,而fildes[1]是用來寫的文件描述符.
在實際使用中我們是通過創建一個子進程,然后一個進程寫,一個進程讀來使用的.
#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);
}
}
為了實現重定向操作,我們需要調用另外一個函數dup2.
#include
int dup2(int oldfd,int newfd);
dup2將用oldfd文件描述符來代替newfd文件描述符,同時關閉newfd文件描述符.也就是說,所有向newfd操作都轉到oldfd上面.下面我們學習一個例子,這個例子將標準輸出重定向到一個文件.
#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);
}
總結
以上是生活随笔為你收集整理的Java实现opendir的api_linux文件操作API的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 公有云安全修炼之路,郭靖和周伯通带你走
- 下一篇: 如何监控Windows进程的句柄资源