Linux系统编程2.文件

来源:互联网 发布:原油api数据影响 编辑:程序博客网 时间:2024/05/17 20:31

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 1024int 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 255int 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 1024int 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);}
1 0