Linux文件I/O(6.1)

来源:互联网 发布:mysql 多字段distinct 编辑:程序博客网 时间:2024/06/10 02:38
/*------IO-day 2--------------------------------------*/
Unix文件基础
一、出错处理:全局错误码errno /*{{{*/
函数出错会置errno,调用者根据errno的不同值可判断出错原因


使用时需要 errno.h 头文件


strerror() - 映射errno对应的错误信息
perror() – 输出用户信息及errno对应的错误信息


注意:只有当调用函数出错才需要检查errno/*}}}*/


二、系统调用及库函数


三、文件IO基础
文件IO :由Unix或linux操作系统内核实现(系统调用中文件操作)相应函数,并提供给应用层调用,实现文件操作,并且不带缓冲/*{{{*/


文件IO中操作对象 : 文件描述符 非负整数,内核用于区分和标识运行的程序中打开的文件


文件IO:不带缓冲 通过 文件描述符  进行访问   open()/read()/write()/lseek()/close()
标准IO:带缓冲   通过 流指针FILE* 进行访问   printf()/fprintf()/fopen()/fread()/fwrite()/fseek()/fclose()

/*}}}*/


文件操作:

一、文件IO相关函数
 
1.打开文件

//需头文件/*{{{*/#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);//功能:打开一个pathname指定的文件,由flags指定打开方式和权限,获得文件描述符
参数:
    pathname        打开文件的路径 "/home/will/test.txt" argv[1]
    flags           打开方式
    O_RDONLY O_WRONLY O_RDWR 三者选一
    O_CREAT 文件不存在创建新文件,还需指定新建文件的权限mode 
    O_TRUNC 文件存在则截短为0
    O_APPEND 每次都以追加方式写


O_EXCL 和O_CREAT 一起使用可测试文件是否存在,存在会出错EEXIST


注意:
1.这些宏可以用 | 连接
2.如果有O_CREAT标志,则需第三个参数mode指定新文件权限


    mode            指定新建文件权限
    注意:实际权限为mode & ~umask

返回值:成功返回当前未使用的最小的文件描述符;失败,返回-1,并置errno

//例:open("/home/will/test.txt",O_RDWR);open(argv[1],O_WRONLY | O_CREAT | O_TRUNC,0666);"r"         -->   O_RDONLY "r+"        -->   O_RDWR"w"         -->   O_WRONLY | O_TRUNC | O_CREAT,0666 "w+"        -->   O_RDWR | O_TRUNC | O_CREAT,0666 "a"         -->   O_WRONLY | O_APPEND | O_CREAT,0666 "a+"        -->   O_RDWR | O_APPEND | O_CREAT,0666
练习:用类似标准IO fopen函数的"w"方式, 打开一个新文件
查看该文件的权限

#include <head.h>int main(int argc, const char *argv[]){int fd_s;if(argc != 2){fprintf(stderr,"usage : %s filename\n",argv[0]);return -1;}if((fd_s = open(argv[1],O_WRONLY | O_CREAT | O_TRUNC,0666)) == -1){perror("error");return -1;}return 0;}
2.关闭文件

#include <unistd.h>/*{{{*/int close(int fd);
功能:关闭打开文件
返回值:成功返回0;出错返回-1,并设置errno/*}}}*


3.读写文件

#include <unistd.h>/*{{{*/

1)读

ssize_t read(int fd, void *buf, size_t count);
功能:从指定文件读指定个数字节
参数:
    fd      指定读的文件
    buf     读入数据存入内存区域首地址
    count   指定读入字符个数 


返回值 :成功,返回读入的字节数,0表示读到文件末尾;失败,返回-1,并置errno
例:

char buf[100];n = read(fd,buf,sizeof(buf));


2)写

ssize_t write(int fd, const void *buf, size_t count);
功能:向指定文件写入指定个数字节
参数:
    fd      指定写的文件
    buf     写入数据存在内存区域首地址
    count   指定写入字符个数 
返回值:成功,返回写入的字节数,0表示一个都未写入;失败,返回-1,并置errno
例:
write(fd,buf,n);
注意:读写都会影响文件表项中的offset,读写完后offset会相应增加有效读写字节数

练习:用read()/write()实现文件拷贝

#include <stdio.h>void do_copy(FILE *fp_s,FILE *fp_d){    int c;    while((c = fgetc(fp_s)) != EOF)        fputc(c,fp_d);    return; }void do_copy_line(FILE *fp_s,FILE *fp_d){    char buf[100];    while((fgets(buf,sizeof(buf),fp_s)) != NULL)        fputs(buf,fp_d);    return; }void do_copy_obj(FILE *fp_s,FILE *fp_d){    char buf[100];    long n = 0;    int cnt = 0;   // 获得原文件的大小     fseek(fp_s,0,SEEK_END);    n = ftell(fp_s);    printf("src length = %ld\n",n);  //创建空洞文件   fseek(fp_d,n-1,SEEK_SET);   fprintf(fp_d," ");   //   getchar(); //定位到文件头   rewind(fp_s);   rewind(fp_d);    while((cnt = fread(buf,sizeof(char),sizeof(buf),fp_s)) != 0)        fwrite(buf,sizeof(char),cnt,fp_d);    return; }// ./a.out src destint main(int argc, const char *argv[]){    FILE *fp_s = NULL;    FILE *fp_d = NULL;    if( argc != 3)    {        fprintf(stdout,"Usage:%s src dest\n",argv[0]);        return -1;    }   // open src file     if((fp_s = fopen(argv[1],"r") ) == NULL)    {      perror("fopen");      return -1;    }   // open dest file     if((fp_d = fopen(argv[2],"w") ) == NULL)    {      perror("fopen");      return -1;    }      //loop read write   //do_copy_line(fp_s,fp_d);   do_copy_obj(fp_s,fp_d);   //close   fclose(fp_s);   fclose(fp_d);    return 0;}
4.定位文件

#include <sys/types.h>/*{{{*/#include <unistd.h>off_t lseek(int fd, off_t offset, int whence);
功能:在打开文件中修改当前文件位置指示器的值
参数:
    fd      指定要定位的文件
    offset  相对偏移量offset > 0,从起点whence向后偏移;offset < 0,从起点whence向前偏移
    whence  相对偏移量起点
        以下三个宏可选
        SEEK_SET    文件开头,offset 只能取正值
        SEEK_CUR    文件位置指示器当前位置,offset可正可负
        SEEK_END    文件末尾,offset可正可负
返回值:成功返回定位后的文件位置偏移量;失败,返回-1L,并置errno

例:

//定位文件读写位置    off_t pos = lseek(fd,-5,SEEK_CUR);//相当于fseek//获得当前偏移量    pos = lseek(fd,0,SEEK_CUR);//相当于ftell//定位到文件开头    lseek(fd,0,SEEK_SET);//rewind//定位到文件结尾    lseek(fd,0,SEEK_END);//求文件长度    off_t len = lseek(fd,0,SEEK_END);
注意:如果offset值大于文件实际大小,又进行了写入操作,会在文件中产生空洞


作业:写日志文件

/*{{{*/
       1. 系统时间获取

#include <time.h>        time_t time(time_t *t);       //功能:获得utc起点到当前时间的秒数      // 传参方法       1.time_t tim = time(NULL);       2.time_t tim;       time(&tim);        //将秒数解释为年月日,时分秒       struct tm *localtime(const time_t *timep);   struct tm {       int tm_sec;   /* seconds */ **       int tm_min;   /* minutes */ **       int tm_hour;   /* hours */   **       int tm_mday;   /* day of the month */ **       int tm_mon;   /* month */ **       int tm_year;   /* year */  **       int tm_wday;   /* day of the week */       int tm_yday;   /* day in the year */       int tm_isdst;   /* daylight saving time */   };       struct tm *tp = localtime(&tim);       tp->tm_sec
2.利用sleep()函数实现每秒写一次

 unsigned int sleep(unsigned int seconds);
/*}}}*/

#include <stdio.h>#include <errno.h>//errno#include <string.h> //strerror#include <time.h>#define N 100int get_line(FILE *fp){    char buf[N]={0};    int line = 0;    while(fgets(buf,sizeof(buf),fp) != NULL)    {        if (buf[strlen(buf) - 1] == '\n')            line++;    }    return line;}void printf_log(FILE *fp){    int line = 0;     time_t tm;    struct tm *ptm;    line = get_line(fp);    while(1)    {        time(&tm);//获取秒数        ptm = localtime(&tm);        fprintf(fp,"%-4d, %04d-%02d-%02d %02d:%02d:%02d\n",            line++,ptm->tm_year+1900,ptm->tm_mon+1,            ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);        fflush(fp);        fprintf(stdout,"%-4d, %04d-%02d-%02d %02d:%02d:%02d\n",            line,ptm->tm_year+1900,ptm->tm_mon+1,            ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);        sleep(1);    }    return ;}//./a.out logint main(int argc, const char *argv[]){    FILE *fp = NULL;    int n = 0;    if(argc != 2)    {        fprintf(stderr,"Usage: %s log\n",argv[0]);        return -1;    }    if ((fp = fopen(argv[1],"a+")) == NULL)    {        fprintf(stderr,"fopen:%s\n",strerror(errno));        return -1;    }#if 0    //统计行号    n = get_line(fp);    printf("n = %d\n",n);#endif     printf_log(fp);    return 0;}
文件目录操作:

打开,读,关闭

1.打开目录

#include <sys/types.h>#include <dirent.h>DIR *opendir(const char *name);//功能:打开一个指定的目录,获得目录流指针//参数://     @name   指定打开的目录//返回值:     //成功,返回可用的目录流;     //失败,返回NULL,并置errno
2.关闭目录

#include <sys/types.h>#include <dirent.h>int closedir(DIR *dirp);//功能:关闭一个打开的目录//参数:      //@dirp        要关闭的目录流//返回值 :     //成功  返回0;     //失败  返回-1,并置errno
3.读目录

#include <dirent.h>struct dirent *readdir(DIR *dirp);//功能:读一个目录(读目录下文件的属性,包括文件名,inode号,文件类型)//参数:   //@dirp  要读的目录流指针//返回值:     //成功,返回目录下一个文件的struct dirent结构首地址,返回NULL,读到结尾并且errno不变;     //出错,返回NULL,并置errno   struct dirent {       ino_t      d_ino;   /* inode number */       off_t      d_off;   /* offset to the next dirent */       unsigned short d_reclen;    /* length of this record */       unsigned char  d_type;   /* type of file; not supported      by all file system types */       char      d_name[256]; /* filename */   };
练习:
读一个指定目录,计数其中普通文件个数,目录文件个数,不包括隐藏文件。
#include <stdio.h>// ./a.out fileint main(int argc, const char *argv[]){    int buf[100] = {0};    int n = 0;    FILE *fp;    if((fp = fopen(argv[1],"r")) == NULL)    {        perror("fopen");        return -1;    }    while((n = fread(buf,sizeof(int),sizeof(buf)/sizeof(int),fp)))    printf("n = %d\n",n);        return 0;}
二、获得文件属性

#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *path, struct stat *buf);//功能:获得文件属性//参数:   //@path    指定文件路径   //@buf     保存属性信息内存区域首地址,传参需定义变量,传首地址//返回值:成功返回0;失败返回-1,并置errno   struct stat {       dev_t st_dev;     /* ID of device containing file */       ino_t st_ino;     /* inode number */       mode_t st_mode;    /* protection */ 关注 mode_t : unsigned int       nlink_t st_nlink;   /* number of hard links */ 关注       uid_t st_uid;     /* user ID of owner */ 关注 uid_t : unsigned int       gid_t st_gid;     /* group ID of owner */ 关注       dev_t st_rdev;    /* device ID (if special file) */       off_t st_size;    /* total size, in bytes */ 关注       blksize_t st_blksize; /* blocksize for file system I/O */       blkcnt_t  st_blocks;  /* number of 512B blocks allocated */       time_t st_atime;   /* time of last access */       time_t st_mtime;   /* time of last modification */ 关注       time_t st_ctime;   /* time of last status change */   };       The following flags are defined for the st_mode field:       //文件类型   S_IFMT     0170000bit mask for the file type bit fields   S_IFSOCK   0140000socket   S_IFLNK    0120000symbolic link   S_IFREG    0100000regular file   S_IFBLK    0060000block device   S_IFDIR    0040000directory   S_IFCHR    0020000character device   S_IFIFO    0010000FIFO   S_ISUID    0004000set UID bit   S_ISGID    0002000set-group-ID bit (see below)   S_ISVTX    0001000sticky bit (see below)       //文件所有者权限   S_IRWXU    00700mask for file owner permissions   S_IRUSR    00400owner has read permission   S_IWUSR    00200owner has write permission   S_IXUSR    00100owner has execute permission       //同组用户权限   S_IRWXG    00070mask for group permissions   S_IRGRP    00040group has read permission   S_IWGRP    00020group has write permission   S_IXGRP    00010group has execute permission       //其它用户权限   S_IRWXO    00007mask for permissions for others (not in group)   S_IROTH    00004others have read permission   S_IWOTH    00002others have write permission   S_IXOTH    00001others have execute permission
获得文件类型方法
st_mode & S_IFMT 可能得到七种不同结果
分别对应七种文件类型
例:

switch(st_mode & S_IFMT){case S_IFSOCK:    putchar('s');    break;case S_IFREG:    putchar('-');    break;}//也可用S_ISREG(st_mode)判断//获得文件权限方法st_mode & S_IRUSRif(st_mode & S_IRUSR){    putchar('r');}else{    putchar('-');}
获得文件所有者的方法
需要st_uid 和 /etc/passwd
利用
getpwuid()
可以实现转换
       #include <sys/types.h>       #include <pwd.h>       struct passwd *getpwuid(uid_t uid);   struct passwd {       char   *pw_name;       /* username */关注       char   *pw_passwd;     /* user password */       uid_t   pw_uid;      /* user ID */       gid_t   pw_gid;      /* group ID */       char   *pw_gecos;      /* user information */       char   *pw_dir;      /* home directory */       char   *pw_shell;      /* shell program */   };//用法:getpwuid(st_uid)->pw_name
获得文件所属组名的方法
需要st_gid和/etc/group
利用
getgrgid()
实现转换
      #include <sys/types.h>       #include <grp.h>       struct group *getgrgid(gid_t gid);   struct group {       char   *gr_name;       /* group name */关注       char   *gr_passwd;     /* group password */       gid_t   gr_gid;      /* group ID */       char  **gr_mem;      /* group members */   };//用法:getgrgid(st_gid)->gr_name
获得文件最后修改时间
st_mtime和localtime()

      #include <time.h>       struct tm *localtime(const time_t *timep);   struct tm {       int tm_sec;   /* seconds */       int tm_min;   /* minutes */       int tm_hour;   /* hours */       int tm_mday;   /* day of the month */       int tm_mon;   /* month */       int tm_year;   /* year */       int tm_wday;   /* day of the week */       int tm_yday;   /* day in the year */       int tm_isdst;   /* daylight saving time */   };//用法:struct tm *tp = localtime(&st_mtime);
练习:利用stat(2)和 readdir(3)函数实现ls -l命令功能
1.实现 ls -l file
类似
$ls -l src 
-rwxr-xr-x 1 will will 7547 Apr 30 15:58 src

2.实现 ls -l dir
类似
$ls -l /home/will/IO/apr30/
-rwxrwxr-x 1 will will  7513 Apr 30 17:05 a.out
-rw-rw-r-- 1 will will  1287 Apr 30 14:11 copy_file.c
-rw-r--r-- 1 will will  1171 Apr 30 16:22 copy_file_io.c
-rw-rw-r-- 1 will will  1024 Apr 30 16:37 dest
drwxrwxr-x 2 will will 32768 Apr 30 09:37 dir
-rw-rw-r-- 1 will will   917 Apr 30 17:08 dir.c
-rw-rw-r-- 1 will will  1061 Apr 30 11:46 fread_fwrite.c
-rw-rw-r-- 1 will will   833 Apr 30 16:42 lseek.c
#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <pwd.h>#include <grp.h>#include <time.h>#include <sys/types.h>#include <dirent.h>#include <string.h>int dispaly_file_stat(const char *path){struct stat f_info;struct tm *ptm;stat(path,&f_info);//file typeswitch(f_info.st_mode & S_IFMT){case S_IFSOCK: putchar('s'); break;case S_IFLNK : putchar('l'); break;case S_IFREG : putchar('-'); break;case S_IFBLK : putchar('b'); break;case S_IFDIR : putchar('d'); break;case S_IFCHR : putchar('c'); break;case S_IFIFO : putchar('p'); break;}//pesimision//ownerf_info.st_mode & S_IRUSR ? putchar('r'):putchar('-');f_info.st_mode & S_IWUSR ? putchar('w'):putchar('-');f_info.st_mode & S_IXUSR ? putchar('x'):putchar('-');//groupf_info.st_mode & S_IRGRP ? putchar('r'):putchar('-');f_info.st_mode & S_IWGRP ? putchar('w'):putchar('-');f_info.st_mode & S_IXGRP ? putchar('x'):putchar('-');//otherf_info.st_mode & S_IROTH ? putchar('r'):putchar('-');f_info.st_mode & S_IWOTH ? putchar('w'):putchar('-');f_info.st_mode & S_IXOTH ? putchar('x'):putchar('-');//nlink printf("%3d ",f_info.st_nlink); //ower nameprintf("%s ",getpwuid(f_info.st_uid)->pw_name);//group nameprintf("%s ",getgrgid(f_info.st_gid)->gr_name);//file sizeprintf("%8ld ",f_info.st_size);//timeptm = localtime(&f_info.st_mtime);printf("%4d-%02d-%02d %02d:%02d:%02d ",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);//file nameprintf("\t%s ",path);printf("\n");return 0;}int dispaly_dir_file(const char *path){    DIR *dir = NULL;struct dirent *pd;char buf[100] = {0};if((dir = opendir(path)) == NULL){perror("opendir");return -1;}while((pd = readdir(dir)) != NULL){//printf("%s\n",pd->d_name);//对文件路径处理,可实现不同目录下的遍历显示if(path[strlen(path) - 1] == '/'){sprintf(buf,"%s%s",path,pd->d_name);}else{sprintf(buf,"%s/%s",path,pd->d_name);}dispaly_file_stat(buf);}closedir(dir);return 0;}// ./a.out fileint main(int argc, const char *argv[]){    if (argc !=2 ){        fprintf(stderr,"Usage:%s file\n",argv[0]);        return -1;    }    //dispaly_file_stat(argv[1]);    dispaly_dir_file(argv[1]);    return 0;}

0 0
原创粉丝点击