文件IO---------1.1系统IO

目录

1.文件IO是什么?

2.Linux中具体的一些API函数接口(系统IO)

    1.打开文件(open)

            ps(权限的指定方法):

                1.使用OS定义的宏标识权限

                2.直接使用八进制数字标识权限

            函数返回值:

                打开成功

                打开失败     

    2.关闭文件(close)

    3.读写文件(read\write)

        3.1write:把自己的数据放到文件里面去

        read:从文件中把数据拿出来

    4.定位文件的光标(偏移量 lseek)

    5.设置文件的掩码(umask)

    6.获取和修改程序的当前工作路径

        如何获取当前工作路径

        修改当前的工作路径:

    7.文件截短(truncate)

    8.删除文件

    9.获取文件的属性(stat)

    10.目录操作

        10.1目录和普通文件的区别

        10.2目录操作的API函数

            a.打开目录(opendir)

            b.读取目录项(readdir)

            c.关闭目录(closedir)



1.文件IO是什么?

        首先我们要了解IO的定义   IO:input  output   指的是对文件的输入和输出操作的基本函数接口
    文件IO分为系统IO和标准IO

    Linux有一个设计思想:Everything is a file in Linux  
    这对对文件的操作接口是很重要的

    文件系统:是用来存储,组织和管理文件的一套方法和规则

    存储文件一般分为两个部分:
        文件的属性:i-node唯一的标识一个文件的存在
        文件本身的内容(用户数据)

    Linux中到底如何组织和存放文件的呢?     如图:
       文件IO---------1.1系统IO_第1张图片
    
    大概步骤:
        struct inode{}
        用来描述一个文件的物理inode信息.系统识别到一个文件的存在,就会为它创建一个struct inode的结构体,一个文件只会唯一的对应一个struct inode

        如果打开了某一个文件
        使用struct file的结构体表示这个打开的文件

        一个文件可以同时被多个进程打开,一个进程也可以同时打开多个文件
        一个进程同时打开了多个文件,意味着需要保存每一个打开的文件的struct file
        使用一个数组保存了所有struct file结构体的地址(结构体指针数组)

        对于用户来说,我们操作文件的时候只需要知道数组的下标,就可以去操作这个文件,这个下标在用户的眼中,叫做文件描述符

        操作文件的内部流程:
            数字(文件描述符)
                ------->
            进程文件表项的内容(结构体指针数组)
                ------->
            struct file 
                ------->
            struct inde 
                ------->
            硬件上面的inode
                ------->
            文件的本身的内容
         
    为了方便,Linux把上面所有的流程都封装起来了,用户不需要知道具体的操作细节
    只需要调用OS提供给我们的API函数接口就可以了

    Linux提供的这些用于操作文件的接口(如:open,read,write...)我们称之为系统IO
    系统IO:操作系统提供给用户操作文件的接口!!!!

2.Linux中具体的一些API函数接口(系统IO)


    注意:
        系统IO提供的API函数接口有很多,在这里我们只提及其中一小部分(注重方法和基础的API)

    1.打开文件(open)

   NAME
            open, openat, creat - open and possibly create a file
                打开或者创建一个文件
        SYNOPSIS
            #include 
            #include 
            #include 

            int open(const char *pathname, int flags);
            int open(const char *pathname, int flags, mode_t mode);

注释:
            pathname:要打开或者创建的文件的路径名(如果不写路径,默认就是程序的当前路径)
                如: "/home/china/1.txt"   or  "1.txt"
            flags:打开文件的标记(使用位域实现,可以添加多个标记)
                O_RDWR      read and write      读写打开
                O_RDONLY    read only           只读打开
                O_WRONLY    write only          只写打开
                以上的三个标记只能选一个

                O_APPEND
                    追加标记,打开文件的时候,文件的偏移量(光标)在文件的末尾
                    默认情况下文件的偏移量在文件开头
                    偏移量可以看做是文件的光标(读和写的位置)

                O_CREAT
                    创建标记,如果文件不存在,则创建这个文件

                O_EXCL  
                    和O_CREAT配合使用,用来测试文件是否存在
                    同时指定这两个标记,文件存在则会报错,并且errno被设置为EEXIST,文件不存在则创建
                
                O_TRUNC 
                    truncate截短,在文件打开的时候,把文件的内容清空

                O_NONBLOCK
                    以非阻塞的方式打开文件,文件的默认打开方式是阻塞打开的
                    阻塞:等待
                        如果文件暂时没有内容可读,read这个文件就会等待(直到有数据可读)
                        如果文件暂时没有空间,write这个文件就会等待(直到有空间)
                    非阻塞:不等待
                        如果文件暂时没有内容可读,read这个文件就不会等待,直接返回一个错误
                        如果文件暂时没有空间,write这个文件不会等待,直接返回一个错误
                ....

                多个标记可以使用  |  连接
                (Linux中的标志大部分都是使用位域实现的,一个整数的某些bit有特殊的含义)

                O_RDWR | O_CREAT | O_TRUNC 
                以读写的方式打开,文件不存在则创建,文件存在则清空内容

            ps(权限的指定方法):


                指定文件的权限,当第二个参数中带有"O_CREAT"时,必须指定创建的文件的权限,有两种指定方式:


                1.使用OS定义的宏标识权限
                    S_IRWXU  00700  user (file owner) has read, write, and execute permission
                    S_IRUSR  00400 user has read permission、
                    S_IWUSR  00200 user has write permission
                    S_IXUSR  00100 user has execute permission
                    S_IRWXG  00070 group has read, write, and execute permission
                    S_IRGRP  00040 group has read permission
                    S_IWGRP  00020 group has write permission
                    S_IXGRP  00010 group has execute permission
                    S_IRWXO  00007 others have read, write, and execute permission
                    S_IROTH  00004 others have read permission
                    S_IWOTH  00002 others have write permission
                    S_IXOTH  00001 others have execute permission

                    U/USR   用户(文件的拥有者)
                    G/GRP   组用户
                    O/OTH   其他用户

                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH 
                    rw-r--r--
                    0644

                2.直接使用八进制数字标识权限


                    0777
                        111 111 111
                        rwx rwx rwx 
                    0664
                        110 110 100
                        rw- rw- r--

            函数返回值:

                打开成功


                    返回打开的文件的文件描述符(进程文件表项的下标),是一个整数
                    >2  &&  int  && 未使用中的最小值
                    因为操作系统会为每一个进程打开三个文件
                        标准输入文件(键盘)  文件描述符  STDIN_FILENO    0
                        标准输出文件(终端)  文件描述符  STDOUT_FILENO    1
                        标准错误文件(终端)  文件描述符  STDERR_FILENO    2
                    后序操作这个文件的时候,就可以直接使用这个数字表示


                打开失败     


                    返回-1,同时errno被设置  
                    errno是一个全局变量,表示的是最后一次调用系统函数出错的错误码    
                    NAME
                        perror - print a system error message
                        打印系统的错误信息
                    SYNOPSIS
                        #include
                        void perror(const char *s); 
                        perror可以把当前的错误码转化为对应的字符串并且打印出来
                        perror("用户提示性字符串:");
                        ======>
                        用户提示性字符串:errno转化之后的错误字符串

                int creat(const char *pathname, mode_t mode);
                int openat(int dirfd, const char *pathname, int flags);
                int openat(int dirfd, const char *pathname, int flags, mode_t mode);

                open("/home/china/1.txt",O_RDWR);
                ----->
                dirfd = open("/home/china",I_RDONLY);
                openat(dirfd,"1.txt",O_RDWR);

    2.关闭文件(close)

        NAME
        close - close a file descriptor
        SYNOPSIS
            #include 
            int close(int fd);

    3.读写文件(read\write)


        3.1write:把自己的数据放到文件里面去


        NAME
            write - write to a file descriptor
        SYNOPSIS
            #include  
            write的作用是把buf指针指向的内存的前面count个字节写入到fd表示的文件中去
            ssize_t write(int fd, const void *buf, size_t count);
            fd:你要把内容写入带哪一个文件中去(open的返回值)
            buf:指针,指向一段内存地址,存储了你要写入的数据
                为什么要使用const呢?
                    从语义来说,在write函数的内部不应该通过buf去修改数据
            count:字节数量,表示你要写入多少个字节
                返回值:
                    >0  返回实际写入到文件中的数据(有可能小于count)
                    =0  表示什么也没写入
                    -1  表示写入失败,同时errno被设置
                    写入的位置位于文件的光标位置

    fd1=write(fd,buf,1024);
    if(fd1==-1)
    {
        perror("write 2.text");
        return -1;
    }    

        read:从文件中把数据拿出来


        NAME
            read - read from a file descriptor
        SYNOPSIS
            #include
            read是从指定的文件中(fd)读取count个字节,存放到buf指向的内存空间中去
            ssize_t read(int fd, void *buf, size_t count);        
            fd:你要从哪一个文件中读取内容(open的返回值)
            buf:指针,指向一段可用的内存空间,表示你要把读取到的数据存放到哪一个位置,不能是野指针,也不能是空指针
            count:字节数量,表示你要读取多少个字节
                返回值:
                    >0  返回实际读取到的数据数量(有可能小于count)
                    =0  表示什么也没读到
                    -1  表示读取失败,同时errno被设置
                    读取的位置位于文件的光标位置

        注意:
            文件的偏移量由内核自动维护,一般来说,打开文件的时候,offset=0
            每一次成功的读和写都会让偏移量改变
            你读/写了count个字节
                offset += count
            在读写文件的时候,要注意文件的光标所在位置

    char buf[1024]={0};
    int fd1=read(fd,buf,1024);
    if(fd1==-1)
    {
        perror("read 1.text");
        return -1;
    }


    4.定位文件的光标(偏移量 lseek)


        NAME
            lseek - reposition read/write file offset
        SYNOPSIS
            #include
            #include
            定位fd表示的文件的偏移量
            off_t lseek(int fd, off_t offset, int whence);
            fd:你要定位的文件的文件描述符
            offset:偏移量,具体的新位置需要结合第三个参数使用
            whence:定义标记,有三种
                SEEK_SET        基于文件开头定位
                    新位置 = 文件开头 + offset(>=0)
                SEEK_CUR        基于文件当前位置定位
                    新位置 = 文件当前光标位置 + offset(可正可负)
                SEEK_END        基于文件结尾定位
                    新位置 = 文件结尾 + offset(可正可负)
            如:
                定位到文件开头
                lseek(fd,0,SEEK_SET);
                定位到文件结尾
                lseek(fd,0,SEEK_END);
        返回值:
            成功返回新光标位置离文件开头的字节数量
            失败返回-1,同时errno被设置

        也可以利用lseek计算文件大小
        size = lseek(fd,0,SEEK_END);

    5.设置文件的掩码(umask)


        umask  表示创建文件时权限的掩码
                创建文件时,不能指定umask中值为1的bit        umask  ------>0002   000 000 010
            在创建文件的时候,不能指定umask中值为1的bit(指定了也会忽略)
            mode = mode & (~umask)
                umask: 0002     000 000 010
                mode: 0777      111 111 111
                &
                ~umask: 0002    111 111 101
                -------------------------------
                                111 111 101
            默认情况下,组用户和其他用户的写权限是不能指定的(0022)
            可以通过命令或者函数修改:

            命令:
                umask + 新的掩码
            函数:
                NAME
                    umask - set file mode creation mask
                SYNOPSIS
                    #include 
                    #include 

                    mode_t umask(mode_t mask);    
                        mask:你要指定的新的文件掩码
                    返回值:
                    返回上一次的文件掩码

    6.获取和修改程序的当前工作路径


        在Linux中,任意一个程序都有一个工作路径
        
        工作路径:在哪一个目录里面运行这个程序,这个程序的工作路径就在哪里(不管你的程序存储在哪一个位置)
        如:
        你有 /home/china/123/abc/a.out 
        当前在: /home/china/123/abc 运行:a.out 
            运行命令: ./a.out 
            工作路径: /home/china/123/abc

        当前在: /home/china   运行:a.out 
            运行命令: ./123/abc/a.out
            工作路径: /home/china
        
        有什么意义呢?
            你如果在a.out中写了
                int fd = open("1.txt", O_RDWR | O_CREAT ,0664);
                这里的 "1.txt" 是相对路径,此相对路径是相对于工作路径而言的
                如果你的工作路径是:/home/china/123/abc
                就会去/home/china/123/abc找1.txt 

                如果你的工作路径是:/home/china
                就会去/home/china找1.txt 


        如何获取当前工作路径


        NAME
            getcwd, getwd, get_current_dir_name - get current working directory
        SYNOPSIS
            #include
            把获取到的工作路径(绝对路径)保存到buf指向的内存空间(必须可用)
            char *getwd(char *buf);
            buf:是用来保存获取到的工作路径的
            返回值:
                成功返回获取到的工作路径字符串的首地址(buf)
                失败返回NULL,同时errno被设置

            警告: the `getwd' function is dangerous and should not be used.
            getwd有一个bug,不应该被使用,可能会造成内存越界
            如果buf指向的空间不够大(当前目录字符串长度超出了buf指向的空间)
            getwd就会访问buf后面的空间(造成数据会误修改)

            getcwd是getwd的升级版本
            char *getcwd(char *buf, size_t size);
            buf:是用来保存获取到的工作路径的
            size:指定了buf可用空间的大小,如果当前的目录长度字符串超过了size-1,这个函数就会报错
            返回值:
                成功返回获取到的工作路径字符串的首地址(buf)
                失败返回NULL,同时errno被设置    

            get_current_dir_name也是获取当前的工作路径,只不过这个函数不需要你给定空间,会在函数内部malloc自动分配足够长的空间,保存获取到的工作路径,并且返回首地址
            所以为了防止内存泄漏,使用者使用完毕之后.应该free这个空间
            char *get_current_dir_name(void);
            返回值:
                成功返回获取到的工作路径字符串的首地址
                失败返回NULL,同时errno被设置        

        修改当前的工作路径:
            改变当前的工作路径
            NAME
                chdir, fchdir - change working directory
            SYNOPSIS
                #include 

                int chdir(const char *path);
                path:要切换到的工作路径的目录字符串
                chdir("/home/china/");

                int fchdir(int fd);
                int fd = open("/home/china/",O_RDONLY) ;
                fchdir(fd);       
            返回值:
                成功会改变当前的工作路径,返回0
                失败返回-1,同时errno被设置


    7.文件截短(truncate)


        NAME
            truncate, ftruncate - truncate a file to a specified length
            截短一个文件到指定的长度
        SYNOPSIS
            #include
            #include

            int truncate(const char *path, off_t length);
            path:   你要截短的文件的路径名(相对路径/绝对路径)
                    绝对路径------>工作路径+path
            length: 截短之后的文件长度
                length < 原来的长度
                    "截短":文件变成指定的长度
                length > 原来的长度 
                    "留空洞"
            返回值:
                成功返回0
                失败返回-1,同时errno被设置

            int ftruncate(int fd, off_t length);
            =====>
            int fd = open(...);
            ftruncate(fd,length);

    8.删除文件


        rm 删除文件 
        rmdir 删除空目录

        unlink  //删除一个普通文件
        rmdir   //删除一个空目录
        remove  //删除一个普通文件或者一个空目录

        NAME
            unlink, unlinkat - delete a name and possibly the file it refers to

        SYNOPSIS
            #include
            int unlink(const char *pathname);
            删除一个文件的时候仅仅只是标记inode没有被使用了

        NAME
            rmdir - delete a directory

        SYNOPSIS
            #include
            int rmdir(const char *pathname);
            必须是空的

        NAME
            remove - remove a file or directory

        SYNOPSIS
            #include

            int remove(const char *pathname);

ps.文件描述符是进程文件表项的下标(数组的下标),是大于0的整数
     系统IO是操作系统提供给用户操作文件的接口(一系列的API函数的统称)


    9.获取文件的属性(stat)


        任何一个文件都有自己的属性(inode中的内容)
        man -a inode 
        NAME
            stat, fstat, lstat, fstatat - get file status

        SYNOPSIS
            #include
            #include
            #include
            stat是用来获取pathname指定的文件的属性的,获取到的属性保存到statbuf指针指向的内存中
            int stat(const char *pathname, struct stat *statbuf);
                pathname:你要获取哪一个文件的属性(路径名)
                statbuf:指针,指向一块可用的空间,用来保存获取到的文件的属性的
            返回值:
                成功返回0,失败返回-1,同时errno被设置

            struct stat *statbuf = NULL;
            int r = stat("1.txt", statbuf);  //ERROR
            ==============>
            struct stat statbuf;  
            //struct stat *p = malloc(sizeof(*p));
            int r = stat("1.txt", &statbuf);
            

            fstat功能和stat类似,只不过需要提供文件描述符,需要提前打开文件
            int fstat(int fd, struct stat *statbuf);
            lstat功能和stat类似,只不过当pathname是一个符号链接的时候,lstat获取的是符号链接本身的属性信息(软连接同样有自己的inode)
            int lstat(const char *pathname, struct stat *statbuf);
            文件B是文件A的符号链接(ln -s A B)
            B----->A 
            stat(B)  获取的是A的inode的信息
            lstat(B)  获取的是B的inode的信息        文件的属性:


            实际上Linux系统是使用一个结构体保存文件的所有属性信息

            struct stat {
               dev_t     st_dev;         /* ID of device containing file */
                    //容纳该文件的设备的设备号码
               ino_t     st_ino;         /* Inode number */
                    //inode号码
               mode_t    st_mode;        /* File type and mode */
                    //保存了文件的权限和类型
               nlink_t   st_nlink;       /* Number of hard links */
                    硬链接数量
               uid_t     st_uid;         /* User ID of owner */
                    文件的所有者ID
               gid_t     st_gid;         /* Group ID of owner */
                    文件的组ID
               dev_t     st_rdev;        /* Device ID (if special file) */
                    如果文件是一个特殊的设备,设备号码
               off_t     st_size;        /* Total size, in bytes */
                    文件的大小(字节数量)
               blksize_t st_blksize;     /* Block size for filesystem I/O */
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

               /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

               struct timespec st_atim;  /* Time of last access */
                    最后访问时间
               struct timespec st_mtim;  /* Time of last modification */
                    最后修改时间(修改了用户数据)
               struct timespec st_ctim;  /* Time of last status change 
               */   最后修改时间 (修改了属性信息,inode中的内容)

                #define st_atime st_atim.tv_sec      /* Backward compatibility */
                #define st_mtime st_mtim.tv_sec
                #define st_ctime st_ctim.tv_sec
           };


        解析权限和类型的方式:

        mode_t    st_mode;        /* File type and mode */
                    //保存了文件的权限和类型
            st_mode使用位域实现,可以使用以下的宏去解析这个变量
            如:
            //获取文件的属性
            struct stat st;  
            int r = stat("1.txt", &st);
            st.st_mode就保存了1.txt的文件的权限和类型
            文件的类型
                S_IFMT     0170000   bit mask for the file type bit field

                S_IFSOCK   0140000   socket
                S_IFLNK    0120000   symbolic link
                S_IFREG    0100000   regular file
                S_IFBLK    0060000   block device
                S_IFDIR    0040000   directory
                S_IFCHR    0020000   character device
                S_IFIFO    0010000   FIFO

                to test for a regular file (for example)
                if ((st.st_mode & S_IFMT) == S_IFREG) 
                {
                    //当前文件是一个普通文件
                }
                或者:
            printf("File type:                ");
            switch (sb.st_mode & S_IFMT) 
            {
            case S_IFBLK:  printf("block device\n");            break;
            case S_IFCHR:  printf("character device\n");        break;
            case S_IFDIR:  printf("directory\n");               break;
            case S_IFIFO:  printf("FIFO/pipe\n");               break;
            case S_IFLNK:  printf("symlink\n");                 break;
            case S_IFREG:  printf("regular file\n");            break;
            case S_IFSOCK: printf("socket\n");                  break;
            default:       printf("unknown?\n");                break;
            }
                或者:
            S_ISREG(m)  is it a regular file?
            S_ISDIR(m)  directory?
            S_ISCHR(m)  character device?
            S_ISBLK(m)  block device?
            S_ISFIFO(m) FIFO (named pipe)?
            S_ISLNK(m)  symbolic link?  (Not in POSIX.1-1996.)
            S_ISSOCK(m) socket?  (Not in POSIX.1-1996.)

            stat(pathname, &st);
            if (S_ISREG(st.st_mode)) 
            {
                //当前文件是一个普通文件
            }    

            文件的权限
            printf("Mode:  %lo (octal)\n",(unsigned long) st.st_mode);    

            if(st.st_mode & S_IRUSR)
            {
                //用户拥有可读的权限
            }else 
            {
                //用户,不拥有可读的权限
            }

       

        但是不管你使用的是哪一个函数(stat/fstat/lstat)都是获取到上面结构体的信息
        注意时间格式转化

        struct timespec 
        {
            time_t   tv_sec;        /* seconds */
            秒,记录的是从1970年1月1日到现在的秒数
            long     tv_nsec;       /* nanoseconds */  //纳秒
        };
            1s == 1000 ms 
            1ms == 1000 us 
            1us == 1000 ns 

        既然你给出的是一个秒数,如何把一个秒数转化为时间呢?

        #include 

        time_t time(time_t *tloc);  //获取本地时间秒数
        char *ctime(const time_t *timep);   //把秒数转化为时间字符串
            timep:你要转化的秒数
            可以把当前的秒数转化为一个表示时间的字符串
            如:
            printf("%s\n",ctime(&st.st_atim.tv_sec));
            or 
            printf("%s\n",ctime(&st.st_atime));

        struct tm *localtime(const time_t *timep);  


            可以把一个当前的秒数转化为一个表示时间的结构体

            struct tm {
               int tm_sec;    /* Seconds (0-60) */
               int tm_min;    /* Minutes (0-59) */
               int tm_hour;   /* Hours (0-23) */
               int tm_mday;   /* Day of the month (1-31) */
               int tm_mon;    /* Month (0-11) */
               int tm_year;   /* Year - 1900 */
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
               int tm_isdst;  /* Daylight saving time */
           };

        如何获取系统时间:
        NAME
            time - get time in seconds
        SYNOPSIS
            #include
            time_t time(time_t *tloc);     
            =============================
            time_t tm = time(NULL);
            or 
            time_t tm;
            time(&tm);

        如何获取更加精准的时间:
        NAME
            gettimeofday, settimeofday - get / set time

        SYNOPSIS
            #include


            //微秒级别的时间


            int gettimeofday(struct timeval *tv, struct timezone *tz);

                struct timeval {
                time_t      tv_sec;     /* seconds */
                suseconds_t tv_usec;    /* microseconds */
                };


            //纳秒级别的时间


            int clock_gettime(clockid_t clk_id, struct timespec *tp);
                struct timespec 
                {
                    time_t   tv_sec;        /* seconds */
                    秒,记录的是从1970年1月1日到现在的秒数
                    long     tv_nsec;       /* nanoseconds */  //纳秒
                };
        练习:
            尝试使用上面的函数(获取文件的属性)

    10.目录操作


        目录在Linux中也是文件,我们能不能按照操作普通文件的方式去操作目录呢?
            普通文件:
                打开文件 
                读写文件 
                关闭文件 
        如果可以,那么目录的内容是什么呢?

        在Linux中,目录也是文件,也可以使用open打开(O_RDONLY)
        也会返回一个文件描述符,但是我们使用read去读取内容的时候,会失败
            read failed: Is a directory
        那么目录文件应该如何操作呢?


        10.1目录和普通文件的区别


            在Linux中,任何一个文件只要存在,就有自己的inode编号
            目录文件也有自己的inode,同样保存了文件的属性信息(stat同样可以获取属性)
            但是目录文件的内容和普通文件的内容有很大的差别
            普通文件的内容就是用户记录的一些用户数据
            目录文件的内容记录的文件和文件之间的组织关系,叫做"目录项"
            可以理解为一个"二维表格",记录着当前目录下面的文件名和inode的对应关系

            在创建一个空目录的时候,系统会自动的为目录预留一个"目录项数组"
            把该目录下面的所有文件(第一层)都记录在这个"数组"中

        10.2目录操作的API函数


            a.打开目录(opendir)


                NAME
                    opendir, fdopendir - open a directory
                SYNOPSIS
                    #include
                    #include

                    DIR *opendir(const char *name);
                    DIR *dir = opendir("/home/china");
                    ========================
                    DIR *fdopendir(int fd);
                    int fd = open("/home/china",O_RDONLY);
                    DIR *dir = fdopendir(fd);

                    name/fd:你要打开的目录的路径名/文件描述符
                    返回值:
                    成功就会返回一个DIR指针(指向当前目录项的指针)
                    失败返回NULL,同时errno被设置
                在Linux中,使用DIR结构体表示一个打开的目录,至于目录里面有什么,不需要关心,只需要知道DIR类型的指针表示一个已经打开的目录就可以了,后序操作这个目录的时候,使用这个指针表示这个目录即可

            b.读取目录项(readdir)


                通过读取目录项,就可以知道目录中有哪些文件了
                NAME
                    readdir - read a directory
                SYNOPSIS
                    #include

                    readdir是用来从dirp指向的目录中,读取下一个"目录项"的指针,一个目录中有多少个"目录项",就有多少个文件

                    每一次调用readdir,都会给你返回一个指向目录项的指针,并且让指针指向下一个目录项(偏移量),直到返回NULL,表示读取完毕
                    struct dirent *readdir(DIR *dirp);
                    dirp:指向你要读取目录项的目录(是opendir的返回值)
                    
                    In  the  glibc  implementation,  the  dirent  structure  isdefined as follows:

    struct dirent {
        ino_t          d_ino;       /* Inode number */
            当前读取到的目录项的inode编号
        off_t          d_off;       /* Not an offset; see below */
        unsigned short d_reclen;    /* Length of this record */
        unsigned char  d_type;      /* Type of file; not supported
                                        by all filesystem types */
            //文件的类型 
        char           d_name[256]; /* Null-terminated filename */
            //文件的名字 
    };
                注意:
                该结构体的成员,只有d_ino,和d_name两个成员是所有文件系统都支持的,如果你想要你的代码有更好的兼容性和可移植性,在代码中尽量只使用这两个成员

                返回值:
               On  success, readdir() returns a pointer to a dirent structure.  (This structure may be statically allocated; do  not attempt to free(3) it.)-------(这意味着它的空间不是在运行时分配的,例如堆栈或空闲存储内存,而是:它在可执行文件本身中,与字符串文字相比,区别在于写入字符串文字是未定义的行为。)

                 If  the  end  of  the  directory stream is reached, NULL is returned and errno is not changed.   
                 If  an  error  occurs, NULL  is  returned and errno is set appropriately.  

            c.关闭目录(closedir)


                NAME
                    closedir - close a directory

                SYNOPSIS
                    #include

                    #include

                    int closedir(DIR *dirp);

你可能感兴趣的:(java,前端,服务器)