zoukankan      html  css  js  c++  java
  • 第三章 文件操作

    底层文件访问

    文件描述符:

    0:标准输入

    1:标准输出

    2:标准错误

    write系统调用

    把缓冲区buf的前nbytes个字节写入与文件描述符fildes关联的文件中。返回实际写入的字节数。返回0表示未写入,返回-1表示错误。

    #include <unistd.h>
    size_t write(int fildes,const void *buf,size_t nbytes)
    #include<unistd.h>
    #include<stdlib.h>
    
    int main(){
        if((write(1,"Here is some data
    ",18))!=18)
          write(2,"A write error has occured on file desciptor 1 
    ",46);
        exit(0);
    }

    输出:

    wuchao@:~/linux_program/CH03$ ./write
    Here is some data

    read系统调用

    从与文件描述符fileds相关联的文件里读入nbytes个字节的数据,并放在buffer中,返回实际读入的字节数。返回0表示未读入,-1表示错误。

    #include <unistd.h>
    size_t read(int fildes, void *buf,size_t nbytes)
    #include<unistd.h>
    #include<stdlib.h>
    
    int main(){
        char buffer[128];
        int nread;
        nread = read(0,buffer,128);
    
        if(nread == -1)
          write(2,"A read error has occurred
    ",26);
    
        if((write(1,buffer,nread)) != nread)
          write(2,"A write error has occurred
    ",27);
    
        exit(0);
    }

    输出:

    wuchao@:~/linux_program/CH03$ echo hello world | ./read
    hello world

    open系统的调用

    为了创建一个新的文件描述符,需要使用open

    #inlcude <fcntl.h>
    int open(const char *path,int oflags);
    int open(const char *path,int oflags,mode_t mode);

    open建立了一条到文件或设备的访问路径。如果调用成功,则返回一个可以被read,write和其他系统调用的文件描述符。即使两个程序打开一个文件,也会得到两个不同的文件描述符。如果它们都对文件写操作,将会各写各的,不会交织,最后数据彼此覆盖,可以使用文件锁解决这种冲突。

    path指定文件名或者设备名,oflags指定打开文件采取的动作。

    oflags参数通过必须文件访问模式域其他可选访问模式结合来指定(多个模式之间用“按位或”结合)。

    以下是oflags的必须访问模式:

    模式 说明
    O_RDONLY 以只读方式打开
    O_WRONLY 以只写方式打开
    O_RDWR 以读写方式打开

    以下是可选访问模式:

    模式 说明
    O_APPEND                   
    把写入数据追加在文件末尾
    O_TRUNC 把文件长度设为0,丢弃已有内容
    O_CREAT 如果需要,就按参数mode中给出的访问模式创建文件
    O_EXCL 与O_CREAT一起使用,确保调用者创建出文件。Open调用是原子操作,只执行一个函数调用。使用这个模式可以防止两个程序同时创建同一个文件。如果文件存在,open调用将失败

    open调用成功时返回一个新的文件描述符,失败返回-1并设置全局变量errno。

     

    当使用O_CREAT标志来使用open创建文件时,必须设置mode参数,该参数是以下几个标志按位或得到:

    mode标志 说明
    S_IRUSR 读权限,文件属user
    S_IWUSR 写权限,文件属user
    S_IXUSR 执行权限,文件属user
    S_IRGRP 读权限,文件属group
    S_IWGRP 写权限,文件属group
    S_IXGRP 执行权限,文件属group
    S_IROTH 读权限,文件属other
    S_IWOTH 写权限,文件属other
    S_IXOTH 执行权限,文件属other

    如下例子:

    open("myfile",O_CREAT,S_IRUSR | S_IXOTH);

    创建一个名为myfile的文件,user拥有读权限,other拥有执行权限

    wuchao@:~/linux_program/CH03$ cat open.c
    #include<unistd.h>
    #include<stdlib.h>
    #include<fcntl.h>
    int main(){
        open("myfile",O_CREAT,S_IRUSR | S_IXOTH);
        exit(0);
    }
    wuchao@:~/linux_program/CH03$ ./open
    wuchao@:~/linux_program/CH03$ ls -l myfile
    -r-------x 1 wuchao wuchao 0 10月 16 10:19 myfile

    注意:mode值将与umask的反值做AND操作,比如上面设置了S_IXOTH,umask的值为001,则文件不会有其他用户的执行权限。

    close系统调用

    终止文件描述符fildes与其对应文件的关联。文件描述符被释放。调用成功返回0,错误返回-1。

    #include<unistd.h>
    close(int fildes);

    ioctl系统调用

    提供了用于控制设备及其描述符行为和配置底层服务的接口。

    #include<unistd.h>
    int ioctl(int fildes ,int cmd,...)

    实验:文件的复制

    将已有的文件file.in复制到新的file.out中

    一个字符一个字符的复制:

    #include<unistd.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<stdlib.h>
    int main(){
            char c;
            int in,out;
            int nread;
    
            in = open("file.in",O_RDONLY);
            out = open("file.out",O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR);
            while((nread = read(in,&c,1))==1){
                    write(out,&c,1);
            }
    }

    按数据块复制:

    #include<unistd.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<stdlib.h>
    int main(){
        char block[20];
        int in,out;
        int nread;
    
        in = open("file.in",O_RDONLY);
        out = open("file.out",O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR);
        while((nread = read(in,block,sizeof(block)))>0){
            write(out,block,nread);
        }
    
        exit(0);
    }

    其他与文件管理有关的系统调用

    lseek

    设置文件的写一个读写位置

    #include<unistd.h>
    #include<sys/types.h>
    off_t lseek(int fildes , off_t offset , int whence)

    offset用来指定位置,whence定义偏移值的用法。

    whence值如下:

    SEEK_SET:offset是一个绝对位置

    SEEK_CUR:offset是相当于当前位置的相对位置

    SEEK_END:相对于文件末尾的相对位置

    lseek函数返回文件头到被设置处的字节偏移值,失败返回-1。

    off_t定义在sys/types.h中

    fstat,stat,lstat

    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    int fstat(int fildes , struct stat *buf);
    int stat(const char *path , struct stat *buf);
    int lstat(const char *path , struct stat *buf);

    stat和lstat通过文件名查询文件的状态信息。当文件是符号链接时,lstat返回符号链接本身的信息,stat返回该链接文件指向的信息。

    取得的文件状态存放在buf指针指向的struct stat结构提中, struct stat的定义如下:

    struct stat    
    {    
        dev_t       st_dev;     /* ID of device containing file -文件所在设备的ID*/    
        ino_t       st_ino;     /* inode number -inode节点号*/  
        mode_t      st_mode;    /* 文件的类型和存取的权限*/    
        nlink_t     st_nlink;   /* number of hard links -链向此文件的连接数(硬连接)*/    
        uid_t       st_uid;     /* user ID of owner -user id*/    
        gid_t       st_gid;     /* group ID of owner - group id*/    
        dev_t       st_rdev;    /* device ID (if special file) -设备号,针对设备文件*/    
        off_t       st_size;    /* total size, in bytes -文件大小,字节为单位*/    
        blksize_t   st_blksize; /* blocksize for filesystem I/O -系统块的大小*/    
        blkcnt_t    st_blocks;  /* number of 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 - */    
    }; 

    st_mode这个变量用来判断文件类型。

    st_mode是用特征位来表示文件类型的,特征位的定义如下:

    S_IFMT      0170000     文件类型的位遮罩  
    S_IFSOCK    0140000     socket  
    S_IFLNK     0120000     符号链接(symbolic link)  
    S_IFREG     0100000     一般文件  
    S_IFBLK     0060000     区块装置(block device)  
    S_IFDIR     0040000     目录  
    S_IFCHR     0020000     字符装置(character device)  
    S_IFIFO     0010000     先进先出(fifo)  
    S_ISUID     0004000     文件的(set user-id on execution)位  
    S_ISGID     0002000     文件的(set group-id on execution)位  
    S_ISVTX     0001000     文件的sticky位  
    S_IRWXU     00700       文件所有者的遮罩值(即所有权限值)  
    S_IRUSR     00400       文件所有者具可读取权限  
    S_IWUSR     00200       文件所有者具可写入权限  
    S_IXUSR     00100       文件所有者具可执行权限  
    S_IRWXG     00070       用户组的遮罩值(即所有权限值)  
    S_IRGRP     00040       用户组具可读取权限  
    S_IWGRP     00020       用户组具可写入权限  
    S_IXGRP     00010       用户组具可执行权限  
    S_IRWXO     00007       其他用户的遮罩值(即所有权限值)  
    S_IROTH     00004       其他用户具可读取权限  
    S_IWOTH     00002       其他用户具可写入权限  
    S_IXOTH     00001       其他用户具可执行权限 

    判断文件类型时,用对文件的st_mode的值与上面给出的值相与,再比较。

    注意:如果要查看文件的权限,可以使用st_mode&S_IRWXU再与S_IXUSR等标志位比较,比如“st_mode&S_IRWXU == S_IXUSR”表示文件只有user的执行权限。

    比如:

    #include <sys/stat.h>  
    #include <unistd.h>  
    #include <stdio.h>  
          
    int main()  
    {  
       int abc;  
       struct stat buf;  
       stat("/home", &buf);  
       abc = buf.st_mode & S_IFDIR;//与对应的标志位相与  
       if(abc == S_IFDIR)          //结果与标志位比较  
         printf("It's a directory.
    ");  
       return 0;  
    }  

    其实还有一个简单的方法,文件类型在POSIX中定义了检查这些类型的宏定义:

    S_ISLINGK(st_mode)      判断是否位符号链接  
    S_ISREG(st_mode)        是否为一般文件  
    S_ISDIR(st_mode)        是否为目录  
    S_ISCHR(st_mode)        是否位字符装置文件  
    S_ISBLK(st_mode)        是否先进先出  
    S_ISSOCK(st_mode)       是否为socket  

    接下来举例,判断文件属性和权限:

    有如下文件

    -rw-rw-r-- 1 wuchao wuchao 123 10月 16 10:27 open.c

    编写代码判断文件类型和user权限

    #include<sys/stat.h>
    #include<unistd.h>
    #include<stdio.h>
    
    int main(){
        int abc;
        struct stat statf;
        stat("open.c",&statf);
        int vDir = statf.st_mode & S_IFDIR;
        int vFile = statf.st_mode & S_IFREG;
        int vRight = statf.st_mode & S_IRWXU;
        if(vDir == S_IFDIR)
            printf("It is a directory 
    ");
        if(vFile == S_IFREG)
                    printf("It is a common file 
    ");
        if(vRight & S_IXUSR)
            printf("this file has excu right by user
    ");
        if(vRight & S_IWUSR)
                    printf("this file has write right by user
    ");
        if(vRight & S_IRUSR)
                    printf("this file has read right by user
    ");
    }

    打印输出

    wuchao@:~/linux_program/CH03$ ./stat
    It is a common file 
    this file has write right by user
    this file has read right by user

    标准I/O库

    头文件stdio.h,与底层文件描述符使用方式一样。在标准I/O库中,与底层文件描述符对应的是流。

    fopen函数

     相当于底层的open。它主要用于文件和终端的输入输出。

    #include<stdio.h>
    FILE *fopen(const char *filename , const char *mode);

    mode参数指定文件的打开方式:

    mode选项 说明
    “r”或"rb" 以只读方式打开
    "w"或“wb” 以写方式打开,并将文件长度截断为零
    “a”或“ab” 以写方式打开,新内容追加在文件尾部
    “r+”或“rb+”或"r+b" 以更新方式打开(读和写)
    "w+"或"wb+"或“w+b” 以更新方式打开,并将文件长度截断为零
    “a+”或“ab+”或“a+b” 以更新方式打开,新内容追加在文件尾部

    b表示是二进制文件而不是文本文件。

    fopen成功时返回非空File指针,失败返回NULL值。

    fread函数

    #include<stdio.h>
    size_t fread(void *ptr , size_t size , size_t nitems , FILE *stream);

    从一个文件流stream里读取数据到ptr指向的缓冲区,size指定每个数据记录的长度,nitems给出要传输的记录个数。返回成功读到缓冲区的记录个数。当到达文件尾时,返回值<=0。

    fwrite函数

    #include<stdio.h>
    size_t fwrite(void *ptr , size_t size , size_t nitems , FILE *stream);

    fclose函数

    int fclose(FILE *stream);

    关闭指定的文件流。

    fflush函数

    将文件流里未写出的数据立刻写出。fclose隐含调用了fclose函数。

    fseek函数

    #include<stdio.h>
    int fseek(FILE *stream , long int offset , int whence);

    在文件流里为下一次读写操作指定位置。

    offset用来指定位置,whence定义偏移值的用法。

    whence值如下:

    SEEK_SET:offset是一个绝对位置

    SEEK_CUR:offset是相当于当前位置的相对位置

    SEEK_END:相对于文件末尾的相对位置

    返回值0表示成功,-1表示失败。

    fgetc,getc,getchar函数

    #include<stdio.h>
    int fgetc(FILE *stream);
    int getc(FILE *stream);
    int getchar();

    fgetc从文件流取出下一个字节并作为字符返回。达到文件末尾或者出现错误时返回EOF。

    fgetc,getc两个都是用来从stream中取得一个字符的,区别在于调用getc函数时所用的参数stream不能是有副作用的表达式(如,不能影响变量),而fgetc函数则可以,也就是说,getc可以被当作宏来调用,而fgetc只能作为函数来调用。 
    一般来说,调用宏比调用函数耗费的时间少。

    fputc,putc,putchar函数

    #include<stdio.h>
    int fputc(int c,FILE *stream);
    int putc(int c,FILE *stream);
    int putchar(int c);

    fputc把一个字节写到一个输出流文件中,返回写入的值,如果失败,返回EOF。

    putchar相当于putc(c,stdout),把单个字符写到标准输出。

    putchar和getchar都是把字符当作int类型而不是char来使用。

    fgets,gets函数

    #include<stdio.h>
    char *fgets(char *s,int n,FILE *stream);
    char *gets(char *s);

    fgets从文件流读入字符串并写到s指向的字符串里,直到遇到以下情况:已经传输了n-1个字符,或者到达文件尾。它会把遇到的换行符也传递到接收参数字符串里,再加上一个表示结尾的空字节,当成功完成时,返回指向字符串s的指针。如果到达文件末尾,返回空指针并设置EOF标识。如果出现错误,fgets返回空指针并设置errno。

    gets类似于fgets,但会丢弃换行符并在尾部加上null字节。

    格式化输入输出

    printf,fprintf,sprintf函数

    #include<stdio.h>
    int printf(const char *format,...);
    int sprintf(char *s,const char *format,...);
    int fprintf(FILE *stream,const char *format,...);

    printf将自己的输出送到标准输出。

    fprintf将自己的输出送到指定的文件流。

    sprintf将自己的输出和一个结尾空字符串写到作为参数传递过来的字符串s里,这个字符串必须能容纳所有输出的数据。

    转换控制符:

    %d:十进制整数

    %o:八进制整数

    %c:输出一个字符

    %s:输出一个字符串

    %f:输出一个浮点数

    %e:以科学计数法输出双精度浮点数

    %g:通用格式输出双精度浮点数

    scanf,fscanf,sscanf函数

    #include<stdio.h>
    int scanf(const char *format,...);
    int fscanf(FILE *stream , const char *format,...);
    int sscanf(const *s,const char *format,...);

    从文件流读取数据,并把数据放到指针参数指向的地址中。

    其他流函数

    fgetpos:获取文件流当前读写位置

    fsetpos:设置文件流当前读写位置

    ftell:返回文件流当前读位置的偏移值

    rewind:重置文件流的读写位置

    freopen:重新使用一个文件流

    setvbuf:设置文件流的缓冲机制

    remove:相当于unlink,如果是目录,则相当于rmdir

    实验:文件的复制

    #include<stdio.h>
    #include<stdlib.h>
    int main(){
        int c;
        FILE *in,*out;
        
        in = fopen("test.file","r");
        out = fopen("test2.file","w");
        
        while((c=fgetc(in)) != EOF)
            fputc(c,out);
        
        fclose(in);
        fclose(out);
        exit(0);
    }

    文件流错误

    错误由外部变量errno指出。

    也可以通过检查文件流状态来确定错误:

    #include<stdio.h>
    int ferror(FILE *stream);
    int feof(FILE *stream);
    void clearerr(FILE *stream);

    ferror测试文件流的错误标识,若有错误,返回非零值,否则返回0。

    feof测试文件流的文件尾标识,到文件尾则返回非0。

    clearerr清除文件流的文件尾表示和错误标识。

    文件和目录的维护

    chmod

    #include<sys/stat.h>
    int chmod(const char *path,mode_t mode);

    参数mode与open系统调用的一样,对所有访问权限通过OR操作。

    chown

    #include<sys/types.h>
    #include<unistd.h>
    int chown(const char *path,uid_t owner,gid_t group);

    unlink,link,symlink

    #include<unistd.h>
    int unlink(const char *path);
    int link(const char *path1,const char *path2);
    int symlink(const char *path1,const char *path2);

    link函数建立硬链接,unlink删除文件的硬链接数,当链接数为零,则文件被删除。即删除path路径的文件。如果文件是符号链接,unlink删除该符号链接(只是删除快捷方式),symlink是符号链接。

    mkdir,rmdir

    #include<sys/types.h>
    #include<sys/stat.h>
    int mkdir(const char *path,mode_t mode);
    #inlcude<unistd.h>
    int rmdir(const char *path);

    注:mode必须符号umask的设置,rmdir只能删除空目录

    chdir,getcwd

    #include<unistd.h>
    int chdir(const char *path);

    切换目录,成功返回0,失败返回-1。

    #include<unistd.h>
    char *getcwd(char *buf,size_t size);

    将当前目录名字放在buf里,如果目录长度超过size,返回NULL。如果成功,返回指针buf。

    扫描目录

    DIR,dirent两种结构体

    struct __dirstream
    {
      void *__fd;
      char *__data;
      int __entry_data;
      char *__ptr;
      int __entry_ptr;
      size_t __allocation;
      size_t __size;
      __libc_lock_define (, __lock)
    };
    typedef struct __dirstream DIR;
    struct dirent
    {
        long d_ino; /* inode number 索引节点号 */
        off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
        unsigned short d_reclen; /* length of this d_name 文件名长 */
        unsigned char d_type; /* the type of d_name 文件类型 */
        char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
    }

    opendir

    include<sys/types.h>
    #include<dirent.h>
    DIR *opendir(const char *name);

    readdir

    #include<sys/types.h>
    #include<dirent.h>
    struct dirent *readdir(DIR *dirp);

    返回一个指针,指向的结构里保存着dirp中下一个目录项的信息。后续的readdir调用将返回后续的目录项。如果发生错误或到目录尾,返回NULL。

    telldir

    #include<sys/types.h>
    #include<dirent.h>
    long int telldir(DIR *dirp);

    返回目录流的当前位置

    seekdir

    #include<sys/types.h>
    #include<dirent.h>
    void seekdir(DIR *sirp,long int loc);

    设置目录流dirp的位置。

    closedir

    #include<sys/types.h>
    #include<dirent.h>
    int close(DIR *dirp);

    关闭目录流。

    实验:扫描并打印目录

     

    /*
     ============================================================================
     Name        : project_01.c
     Author      : wuchao
     Version     :
     Copyright   : njupt
     Description : Hello World in C, Ansi-style
     ============================================================================
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
    
    void printDir(char *dir,int depth){
        struct dirent *entry;
        struct stat statbuf;
        DIR *dp;
    
        //检查该目录能否打开
        if((dp=opendir(dir))==NULL){
            fprintf(stderr,"cannot open directory:%s 
    ",dir);
            return;
        }
        //切换到该目录
        chdir(dir);
        //循环读该目录的内容,读到目录尾部时返回NULL
        while((entry = readdir(dp))!=NULL){
            lstat(entry->d_name,&statbuf);//读取文件或目录的详细信息
            //判断是文件还是目录
            if(S_ISDIR(statbuf.st_mode)){
                //不打印“.”和“..”
                if(strcmp(".",entry->d_name)==0||strcmp("..",entry->d_name)==0){
                    continue;
                }
                printf("%*s%s/
    ",depth,"",entry->d_name);
                printDir(entry->d_name,depth+4);
            }else{
                //如果是目录,直接打印,然后继续读该目录的下一个内容
                printf("%*s%s
    ",depth,"",entry->d_name);
            }
        }
        //读完该目录后,返回上一层目录
        chdir("..");
        //关闭目录流
        closedir(dp);
    }
    int main(void) {
        char *path = "/home/wuchao/caffe/docs";
        printf("Directory scan of %s:/n",path);
        printDir(path,0);
        return EXIT_SUCCESS;
    }

    错误处理

    系统调用和函数都会产生各种错误而失败,此时会设置外部变量errno的值。errno的取值定义在errno.h文件,如下:

    EPERM:操作不允许

    ENOENT:文件或目录不存在

    EIO:I/O错误

    EBUSY:设备或资源忙

    EEXIST:文件存在

    EINVAL:无效参数

    EMFILE:打开的文件过多

    EISDIR:是一个目录

    ENODEV:设备不存在

    ENOTDIR:不是一个目录

    以下两个函数可以用来报告错误

    streeror函数

    #include<string.h>
    char *streeror(int errnum);

    perror函数

    #include<stdio.h>
    void perror(const char *s);

    将字符串s和错误信息以”:“号连接,并输出到标准输出中。

     

  • 相关阅读:
    Unity3D 中的灯光与渲染
    利用正态分布(高斯分布)绘制噪点图
    Unity3D 调用相机
    Unity3D开发安卓应用如何设置横屏显示和竖屏显示
    Unity3D中Excel表的读取与写入
    Unity3D启动外部程序并传递参数
    Unity动画系统Animator动态添加事件
    Unity3D编辑器扩展(六)——模态窗口
    Unity3D编辑器扩展(五)——常用特性(Attribute)以及Selection类
    Unity3D编辑器扩展(四)——扩展自己的组件
  • 原文地址:https://www.cnblogs.com/wuchaodzxx/p/5965464.html
Copyright © 2011-2022 走看看