zoukankan      html  css  js  c++  java
  • linux应用编程一:文件IO和目录操作

    1.文件IO和标准IO
      文件IO: 遵照POSIX(可移植操作系统接口)规则,无缓冲,通过文件描述符操作。
      标准IO: 遵照ANSIC规则,带缓冲区,流FILE,
      Linux下,标准IO基于文件IO

    2.文件描述符
      每个打开的文件都对应一个文件描述符;
      文件描述符是一个非负数。linux为程序中每个打开的文件分配一个文件描述符
      文件描述符从0开始分配,依次递增。

      每个程序分配的文件描述符都是从0开始,不同程序不影响。
      标准输入,标准输出和标准错误分别占用0,1和2。

    3.打开文件
      open函数用开创建或打开一个文件

      #include "fcontl.h"
      int open(cosnt char *path, int oflag, ...);

      path: 要打开的文件的路径。
      oflag: 指定打开方式
        O_RDONLY : 只读方式打开
        O_WRONLY : 可写方式打开
        O_RDWR : 读写方式打开
      注:这三个参数互斥,只能设置一个。
        O_CREAT : 如果文件不存在,就创建一个新文件,此时必须设置第三个参数用于权限设置。
        O_EXCL : 如果使用O_CREAT是文件存在,则返回错误消息。这一参数可测试文件是否存在。
        O_TRUNC : 如果文件已经存在,那么打开文件时先删除源文件数据
        O_APPEND : 以添加的方式打开文件,所以对文件的写操作都在文件的末尾进行。

      返回值: 返回文件的文件描述符。出错时返回EOF。
      打开文件时,使用两个参数。
      创建文件是第三个参数指定新文件的权限(具体的权限与umask有关)。
      注:只能打开设备文件。

      例子:
        以只写的方式打开文件1.txt,如果文件不存在则创建,如果文件存在则清空。

            int fd;
            if((fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666) < 0)
            {
                perror("open");
                return -1;
            }

        以只读的方式打开文件1.txt,如果文件不存在则创建,如果文件存在则报错。

            int fd;
            if((fd = open("1.txt", O_RDONLY | O_CREAT | O_EXCL, 0666) < 0)
            {
                if(error == EEXIST)
                {
                    perror("exist error");
                }
                else
                {
                    perror("other error");
                }
                return -1;
            }

    4.关闭文件
      close函数用开关闭一个打开的文件
      #include <unistd.h>
      int close(int fd);

      fd : 文件标识符
      成功返回0;出错是返回EOF。
      文件一旦关闭就不能在使用这个文件了。

    5.读取文件
      read函数用来从文件中读取数据:
      #include <unistd.h>
      ssize_t read(int fd, void *buf, size_t count);

      fd: 文件标识符
      buf: 缓冲区首地址
      count: 读取大小,不能超过缓冲区的大小,一般指定为缓冲区的大小。

      返回值:成功是返回实际读取的字节数;出错返回EOF。
      读到文件末尾返回0;

    6.写文件
      write函数用来从文件中读取数据:
      #include <unistd.h>
      ssize_t write(int fd, void *buf, size_t count);

      fd: 文件标识符
      buf: 缓冲区首地址
      count: 指定写入的个数,一般指定为缓冲区的大小或者小于buf的大小。

      返回值:成功是返回实际写入的字节数;出错返回EOF。成功返回值等于第三个参数。

    7.文件定位
      lseek函数用于定位文件
      #include <unistd.h>
      off_t lseek(int id, off_t offset, intt whence);

      id : 文件描述符
      offset 指的是相对于whence的偏移量,可正可负,不能小于0。
      whence 预定义宏,
        SEEK_SET 文件开头
        SEEK_CUR 当前位置
        SEEK_END 文件结尾

      返回值:
        成功返回当前的文件读写位置,出错返回EOF。

            #include <stdio.h>
            #include <unistd.h>
            #include <fcntl.h>
            #include <string.h>
    
            #define N 64
    
            int main(int argc, char const *argv[])
            {
                /* code */
                int fds, fdt, n;
                char buf[N];
    
                if(argc != 3)
                {
                    printf("usage :%s <str_file> <dst_file> 
    ", argv[0]);
                    return -1;
                }
    
                if((fds = open(argv[1], O_RDONLY)) == -1)
                {
                    fprintf(stderr, "open %s : %s error 
    ", argv[1],strerror(error));
                    return -1;
                }
                if((fdt = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1)
                {
                    fprintf(stderr, "open %s : %s error 
    ", argv[2],strerror(error));
                    //关闭第一个已经打开的文件
                    close(fds);
                    return -1;
                }
    
                while( (n = read(fds, buf, N)) > 0)
                {
                    write(fdt,bug,n);
                }
    
                close(fds);
                close(fdt);
    
                return 0;
            }

    8.文件知识补充
    1、硬盘中的静态文件和inode(i节点)
      (1)文件平时都在存放在硬盘中的,硬盘中存储的文件以一种固定的形式存放的,我们叫静态文件。
      (2)一块硬盘中可以分为两大区域:一个是硬盘内容管理表项,另一个是真正存储内容的区域。操作系统访问硬盘时是先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去查询真正存储内容的区域,最后得到我们要的文件。
      (3)操作系统最初拿到的信息是文件名,最终得到的是文件内容。第一步就是去查询硬盘内容管理表,这个管理表中以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表,我们叫inode,i节点(其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)
    强调:硬盘管理的时候是以文件为单位的,每个文件一个inode,每个inode有一个数字编号,对应一个结构体,结构体中记录了各种信息。
      (4)联系实际,格式化硬盘(U盘)时发现有:快速格式化和底层格式化。
    快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?
    快速格式化只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。
    2、内存中被打开的文件和vnode(v节点)
      (1)在程序中打开的文件就属于某个进程。每个进程都有一个数据结构用来记录这个进程的所有信息(叫进程信息表),表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。文件管理表中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode
      (2)vnode中就记录了一个被打开的文件的各种信息,我们只要知道这个文件的fd,就可以很容易的找到这个文件的vnode进而对这个文件进行各种操作。

    9.文件共享

    1、文件共享就是同一个文件(指的是同一个inode,同一个pathname)被多个独立的读写体(可以理解为多个文件描述符)去同时(一个打开尚未关闭的同时另一个去操作)操作。

    2、3种文件共享的情况:

      第一种是同一个进程中多次使用open打开同一个文件

      第二种是在不同进程中去分别使用open打开同一个文件(这时候因为两个fd在不同的进程中,所以两个fd的数字可以相同也可以不同)

      第三种情况是后面要学的,linux系统提供了dup和dup2两个API来让进程复制文件描述符。

    10 复制文件描述符

    1、dup和dup2函数介绍
      #include <unistd.h>

      int dup(int oldfd);
      int dup2(int oldfd, int newfd);

      oldfd:要复制的文件描述符

      返回值:返回一个新的文件描述符

      区别:dup返回的文件描述符由系统自动分配,dup2可以指定新的文件描述符。

      使用案例:可以将close(1)与dup配合使用进行输出重定位。

      使用复制文件描述符的方式进行操作时时接续写。

    二 目录操作

    1.打开目录

      opendir函数用开打开一个目录文件
      #include <dirent.h>
      DIR *opendir(const char *name);

      DIR是用来描述一个打开的目录文件的结构体类型。
      成功返回目录流指针;失败返回NULL。

      注:

        struct __dirstream
        {
          void *__fd; /* `struct hurd_fd' pointer for descriptor. */
          char *__data; /* Directory block. */
          int __entry_data; /* Entry number `__data' corresponds to. */
          char *__ptr; /* Current pointer into the block. */
          int __entry_ptr; /* Entry number `__ptr' corresponds to. */
          size_t __allocation; /* Space allocated for the block. */
          size_t __size; /* Total valid data in the block. */
          __libc_lock_define (, __lock) /* Mutex lock for this structure. */
        };
        typedef struct __dirstream DIR;

    2.读取目录内容:

      readdir函数用来读取目录流中的内容
      #include <dirent.h>
      struct dirent *readdir(DIR *dirp);

      dirent 用来描述目录流中一个目录项的结构体类型
        包含成员 char d_name[256]  文件名
      成功时返回目录流dirp中下一个目录项;
        出错或到末尾是返回NULL。

      注:

            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字符 */
            }

    3.关闭目录

      closedir函数用于关闭一个目录文件。
      #include <dirent.h>
      int closedir(DIR *dirp);

      成功返回0;出错时返回EOF。

    三 文件权限

    1.修改文件访问权限

      chmod 和 fchmod 用来修改文件的访问权限。
      #include <sys/stat.h>
      int chmod(const char *path, mode_t mode);
      int fchmod(int fd, mode_t mode);

      成功返回0,出错时返回EOF。
        只有root和文件的所有者才能修改文件的权限。

    2.获取文件的属性

      stat/lstat/fstat函数
      #include <sys/stat.h>
      int stat(const char *path, struct stat *buf);
      int lstat(const char *path, struct stat *buf);
      int fstat(int fd, struct stat *buf);

      成功返回0,出错时返回EOF。
      stat 和 lstat 的区别:
        当文件是一个链接文件,stat返回链接文件指向的文件的属性,而lstat指向的是链接文件本身的属性。
        其他情况功能一致。
      fstat第一个参数为文件描述符,操作要先打开文件。

        struct stat常用属性
            mode_t st_mode;            类型和访问权限
            uid_t st_uid;            所有者id
            uid_t st_gid;            用户组id
            off_t st_size;            文件大小
            time_t st_mtime;        最后修改时间
    
        st_mode:
            该成员描述了文件的类型和权限两个属性。
            st_mode是个32位的整型变量,不过现在的linux操作系统只用了低16位
    
            在<sys/stat.h>中,有如下定义。
            File type属性区域,位于bit12 ~ bit15.
            #define S_IFMT  00170000  
            #define S_IFSOCK 0140000  
            #define S_IFLNK  0120000  
            #define S_IFREG  0100000  
            #define S_IFBLK  0060000  
            #define S_IFDIR  0040000  
            #define S_IFCHR  0020000  
            #define S_IFIFO  0010000  
            #define S_ISUID  0004000  
            #define S_ISGID  0002000  
            #define S_ISVTX  0001000  
              
            #define S_ISLNK(m)      (((m) & S_IFMT) == S_IFLNK)      //符号链接文件(symbolic link)
            #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)      //普通文件(regular file)
            #define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)      //目录(directory)
            #define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)      //字符设备(character device)
            #define S_ISBLK(m)      (((m) & S_IFMT) == S_IFBLK)      //块设备(block device)
            #define S_ISFIFO(m)     (((m) & S_IFMT) == S_IFIFO)      //管道(FIFO<pipe>)
            #define S_ISSOCK(m)     (((m) & S_IFMT) == S_IFSOCK)      //套接口文件(socket)
    
                
            例子:
                判断一个文件是不是文件夹
                    if ((info.st_mode & S_IFMT) == S_IFDIR)  
                        printf("this is a directory");
                或
                    if (S_ISDIR(info.st_mode))  
                        printf("this is a directory");
    
    
               Permission属性区域的bit0~bit8,也即st_mode字段的最低9位,代表文件的许可权限,
               它标识了文件所有者(owner)、组用户(group)、其他用户(other)的读(r)、写(w)、执行(x)权限。
    
               在<sys/stat.h>中,有如下定义:
                   #define S_IRWXU 00700   /* mask for file owner permissions */  
                #define S_IRUSR 00400   /* owner has read permission */  
                #define S_IWUSR 00200   /* owner has write permission */  
                #define S_IXUSR 00100   /* owner has execute permission */  
                  
                #define S_IRWXG 00070   /* mask for group permissions */  
                #define S_IRGRP 00040   /* group has read permission */  
                #define S_IWGRP 00020   /* group has write permission */  
                #define S_IXGRP 00010   /* group has execute permission */  
                  
                #define S_IRWXO 00007   /* mask for permissions for others (not in group) */  
                #define S_IROTH 00004   /* others have read permission */  
                #define S_IWOTH 00002   /* others have write permission */  
                #define S_IXOTH 00001   /* others have execute permission */  
    
               例子:
                   打印出文件的权限,"-rx-rx-rx"
                   for(int n = 8; n >= 0; n--)
                   {
                       if(buf.st_mode & (1 << n))
                       {
                           switch(n & 3):
                           {
                               case 2:
                                   printf("r");
                                   break;
                               case 1:
                                   printf("w");
                                   break;
                               case 0:
                                   printf("x");
                                   break;
                           }
                       }
                       else
                       {
                           printf("-");
                       }
                   }

      注:目录的权限与普通文件的权限是不同的。目录的读、写、执行权限含义分别如下:
      (1)读权限。读权限允许我们通过opendir()函数读取目录,
        进而可以通过readdir()函数获得目录内容,即目录下的文件列表。
      (2)写权限。写权限代表的是可在目录内创建、删除文件,而不是指的写目录本身。
      (3)执行权限。可访问目录中的文件。

    Ps:文件参考:man手册及《朱有鹏课程笔记》

  • 相关阅读:
    Java 判断回文字符串有多少和其中的最大字符串
    多线程下并发数据结构
    HashMap底层及使用个人理解
    简单预览课本后的疑问
    自我介绍
    HTML中行内元素与块级元素的区别:
    html基本选择符的使用
    Html简单介绍
    Sublime Text 3 快捷键精华版
    html文本的基本设置
  • 原文地址:https://www.cnblogs.com/longxi/p/13439863.html
Copyright © 2011-2022 走看看