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);
//pathname 打开的文件名(包含路径名称,缺省:在当前路径下面).
//flags 可以去下面的一个值或者是几个值的组合.
/* (前三个标志是唯一标志只能用一个)
O_RDONLY:以只读的方式打开文件.
O_WRONLY:以只写的方式打开文件.
O_RDWR:以读写的方式打开文件.
O_APPEND:以追加的方式打开文件.
O_CREAT:创建一个文件.
O_EXEC:如果使用了 O_CREAT 而且文件已经存在,就会发生一个错误.
O_NOBLOCK:以非阻塞的方式打开一个文件.
O_TRUNC:如果文件已经存在,则删除文件的内容.
*/
int open(const char *pathname,int flags,mode_t mode);
/*
如果使用了 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
-------------------------------------------------------
*/
int close(int fd);
>我们也可以用数字来代表各个位的标志.Linux 总共用 5 个数字来表示文件的各种权限.
00000.第一位表示设置用户 ID.第二位表示设置组 ID,第三位表示用户自己的权限位,第四Linux 操作系统位表示组的权限,最后一位表示其他人的权限.
每个数字可以取 1(执行权限),2(写权限),4(读权限),0(什么也没有)或者是这几个值的和
>>比如:创建一个用户读写执行,组没有权限,其他人读执行的文件.设置用户 ID 位那么
我们可以使用的模式是–1(设置用户 ID)0(组没有设置)7(1+2+4)0(没有权限,使用缺省)
5(1+4)即 10705:
>>open(“temp”,O_CREAT,10705);
打开文件成功,open 会返回一个文件描述符,对文件的所有操作就可以通过
对这个文件描述符进行操作了
文件打开了以后,就要对文件进行读写了,可以调用函数 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 要读写的字节数.
*/
实例:
#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. 文件的各个属性
文件具有各种各样的属性,除了文件的权限,还有创建时间,大小等属性。
判断文件是否可以进行某种操作(读、写等)。使用access函数。
int access(const char *pathname,int mode)
/*
pathname:文件名称,
mode :判断的属性.
可以取
R_OK 文件可以读,
W_OK 文件可以写,
X_OK 文件可以执行,
F_OK 文件存在.
或者是他们的组合.成功时 ,函数返回 0,否则如果有一个条件不符时,返回-1.
*/
如果我们要获得文件的其他属性,我们可以使用函数 stat 或者 fstat。
#include <unistd.h>;
int stat(const char *file_name,struct stat *buf);
//stat 用来判断没有打开的文件
int fstat(int filedes,struct stat *buf);
//fstat 用来判断打开的文件
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; /* 最后一次改变时间(指属性) */
};
使用最多的属性是 st_mode,通过st_mode属性可以判断给定的文件是一个普通文件还是一个目录,连接等等.可以 使用下面几个宏来判断.
S_ISREG 是否是一个常规文件.
S_ISDIR 是否是一个目录 .
S_ISCHR 是否是一个字符设备.
S_ISBLK 是否是一个块设备 .
S_ISFIFO 是否 是一个 FIFO文件.
S_ISSOCK 是否是一个 SOCKET 文件.
3. 目录文件的操作
c库函数提供了getcwd来获取当前工作路径。
#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);
/*telldir 和 seekdir 类似与 ftee 和 fseek 函数*/
int closedir(DIR *dir);
/*关闭目录*/
struct dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[NAME_MAX+1]; /* 文件名称 */
}
下面有一个实例,这个实例有一个参数。如果这个参数是一个文件名,我们输出这个文件的大小和最后修改的时间,如果是一个目录我们输出这个目录下所有文件的大小和修改时间。
#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)/*获取文件属性失败,返回-1*/
{
printf("Get stat on %s Error: %s\n",
filename,strerror(errno));
return(-1);
}
if(S_ISDIR(statbuf.st_mode))/*是一个目录,返回1*/
return(1);
if(S_ISREG(statbuf.st_mode))/*是一个文件,打印文件大小,返回0*/
printf("文件: %s 大小: %ld bytes\t修改时间: %s",
filename,statbuf.st_size,ctime(&statbuf.st_mtime));
return(0);
}
int main(int argc,char **argv)/*argc是参数个数+1 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 调用可以创建一个管道(通信缓冲区).当调用成功时,可以访问文件描述符 fildes[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]);/*关闭文件1*/
printf("Child[%d] Write to pipe\n\a",getpid());
snprintf(buffer,BUFFER,"%s",argv[1]);
write(fd[1],buffer,strlen(buffer));/*buffer中内容写入文件2*/
printf("Child[%d] Quit\n\a",getpid());
exit(0);
}
else/*不是子进程*/
{
close(fd[1]);/*关闭文件2*/
printf("Parent[%d] Read from pipe\n\a",getpid());
memset(buffer,'\0',BUFFER+1);/*buffer[BUFFER+1] = '/0' */
read(fd[0],buffer,BUFFER);/*读文件1到buffer*/
printf("Parent[%d] Read: %s\n",getpid(),buffer);
exit(1);
}
}
为了实现重定向需要调用
#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);/*从stdin读入buffer*/
if(feof(stdin))/*读到stdin*/
break;
write(STDOUT_FILENO,buffer,strlen(buffer));/*写入STDOUT_FILENO(STDOUT_FILENO已经被重定向)*/
}
exit(0);
}