Linux文件编程2-基础IO

文件描述符

文件描述符是计算机操作文件时所用的符号,上文中所提到的i节点是计算机查找文件时所用的符号。那么,既然已经有i节点号可以唯一确定文件,为什么呢还要用文件描述符呢?一个文件可以被同一个用户或者不同用户同时打开一次或多次,这就造成文件需要有一个结构能够分别记录每一次打开后的操作,比如光标位置。因此,linux系统使用了文件描述符来解决这个问题。当打开或者创建一个文件的时候,内核就会向进程返回一个文件描述符(非负整数),所有对文件的后续操作都通过该文件描述符来完成。一个i节点可以对应多个文件描述符,每一个文件描述符都代表了一次对该文件的打开操作。系统的文件描述符是有限的,打开文件后一定要记得关闭,及时将文件描述符释放。

文件的基础IO

文件的创建与打开

  1. 函数

    #include <fcntl.h>
    /**
    * @param pathname  文件的路径
    * @param flags     打开方式
    * @param mode      创建文件时,设置文件的权限 
    * @return int      打开/创建成功时返回文件描述符,否则返回-1
    */
    int open(const char *pathname, int flags, mode_t mode);   // 创建文件
    int open(const char *pathname, int flags);                // 打开文件
    
    flags意义
    O_RDONLY只读
    O_WRONLY只写
    O_RDWR读写
    O_APPEND追加
    O_CREAT如果文件不存在则创建
    O_EXCL如果使用O_CREAT时文件已经存在,则强制open失败
    O_TRUNC打开文件时清空文件内容
    O_DSYNC每次写入时等待数据写到磁盘上
    O_RSYNC每次读取时,等到相同部分先写到磁盘上
    O_SYNC以同步方式写入文件,强制刷新内核缓冲区到输出文件
    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文件其他用户组的读写和执行权限

    flags的选项可以通过 | 联合使用,例如:

    #include<sys/stat.h>             // mode宏的头文件
    #include <fcntl.h>               // flags宏的头文件
    int flags = O_CREAT | O_RDONLY;  // 如果文件不存在则创建文件,并以只读的方式打开文件
    
  2. 演示

  • 创建文件时不配置mode权限会发生什么

    /*
    * name:     20230918_1.cpp
    * funtion:  无权限文件创建
    * Date:     2023-09-18
    * Author:   Torch-HXM
    */
    
    #include <iostream>
    #include <fcntl.h>
    #include <sys/stat.h>
    
    using namespace std;
    
    int main(){
    char filePath[] = "./20230918_1.txt";
    int flags = O_CREAT | O_RDWR;
    int fd = open(filePath, flags);
    
    if(fd){
            cout<< "create file successfully."<< endl;
    }
    else{
            cout<< "file created failed."<< endl;
            return -1;
    }
    
    return 0;
    }
    

    在上面的代码中,由于我们没有文件"./20230918_1.txt",故而flags的O_CREAT部分会让系统创建这个文件,O_RDWR让系统以读写的方式打开文件。我们将代码编译并执行

    $ g++ 20230918_1.cpp
    $ ls
    20230918_1.cpp  a.out
    $ ./a.out
    create file successfully.
    $ ls
    20230918_1.cpp  20230918_1.txt  a.out
    

    可以看到文件被成功的创建,接下来我们查看文件内容:

    $ cat 20230918_1.txt
    cat: 20230918_1.txt: Permission denied
    $ ls -l
    total 16
    -rw-r--r-- 1 torch torch  415 Sep 18 14:49 20230918_1.cpp
    ---x------ 1 torch torch    0 Sep 18 14:51 20230918_1.txt
    -rwxr-xr-x 1 torch torch 9024 Sep 18 14:51 a.out
    

    可以看到,当我们使用cat命令输出文件内容时,系统提示我们没有查看文件的权限。我们使用ls -l查看文件的权限信息,发现,文件所有者仅仅拥有对文件的执行权限。经过测试这个执行权限,仅允许我们在执行代码时打开文件,而不能够读取和写入文件内容。

  • 创建一个有权限的文件

    接下来我们修改代码,删除生成的a.out和20230918_1.txt文件。

    /*
    * name:    20230918_1.cpp
    * funtion: 有权限文件创建
    * Date:    2023-09-18
    * Author:  Torch-HXM
    */
    
    #include <iostream>
    #include <fcntl.h>
    #include <sys/stat.h>
    
    using namespace std;
    
    int main(){
    char filePath[] = "./20230918_1.txt";
    int flags = O_CREAT | O_RDWR;
    mode_t mode = S_IRWXU;                  // 给文件所有者读写和执行权限
    int fd = open(filePath, flags, mode);
    
    if(fd){
            cout<< "create file successfully."<< endl;
    }
    else{
            cout<< "file created failed."<< endl;
            return -1;
    }
    
    return 0;
    }
    

    编译并执行代码后,使用cat命令查看20230918_1.txt文件,不再出现权限问题,查看文件权限:

    $ ls -l
    total 16
    -rw-r--r-- 1 torch torch  521 Sep 18 15:06 20230918_1.cpp
    -rwx------ 1 torch torch    0 Sep 18 15:03 20230918_1.txt
    -rwxr-xr-x 1 torch torch 9024 Sep 18 15:03 a.out
    

    20230918_1.txt文件的所有者已经具有读写和执行权限。所以,在创建文件时,一定要设置权限,不然,即使创建成功,即使flags中规定以读写的方式打开,即使程序不报错,也会因为权限问题无法读写成功。

  • 打开文件

    • 如果打开文件时使用了mode参数会发生什么?
      我们在已经拥有文件"./20230918_1.txt"的情况下再次执行程序,发现程序没有报错。我们修改程序中赋予的权限mode为读权限,编译后执行程序,发现"./20230918_1.txt"文件的权限仍然是读写和执行,并没有变为读,因此判定当文件存在时,mode参数失去作用,加上该参数不会报错也不会对文件的权限进行修改。
    • 使用不带mode参数的open函数打开文件和带mode参数的open函数打开文件的效果一致。
  • 文件的关闭
    系统的文件描述符数量是有限的,在程序中打开文件后一定要记得关闭。我们在文件的创建与打开中的示例在打开文件后并没有关闭文件的步骤,这样会使得内核分配给我们的文件描述符无法被释放(重启后自动释放)。

    • 函数

      #include <unistd.h>
      /**
       * @param fd   要关闭文件的文件描述符
       * @return int 成功关闭时返回0,否则返回-1
       */
      int close(int fd);
      
    • 演示

      /*
       * name:    20230918_1.cpp
       * funtion: 关闭文件
       * Date:    2023-09-18
       * Author:  Torch-HXM
      */
      
      #include <iostream>
      #include <fcntl.h>
      #include <unistd.h>
      #include <sys/stat.h>
      
      using namespace std;
      
      int main(){
          char filePath[] = "./20230918_1.txt";
          int flags = O_CREAT | O_RDWR;
          int fd = open(filePath, flags);
      
          cout<< "文件描述符为:"<< fd<< endl;
      
          if(fd){
              cout<< "open file successfully."<< endl;
          }
          else{
              cout<< "open file failed."<< endl;
              return -1;
          }
      
          fd = close(fd);
      
          if(fd==0){
              cout<< "close file successfully."<< endl;
          }
          else{
              cout<< "close file failed."<<endl;
              return -1;
          }
      
          return 0;
      }
      
  • 写入和读取文件

    • 函数

      #include <unistd.h>
      /**
       * @param fd      文件描述符
       * @param buf     字符缓冲区,储存着即将写入到文件中的字符
       * @param count   写入数量,从buf中写入count个字符到文件中
       * @return int    执行成功则返回写入的字节数,否则返回-1
       */
      ssize_t write(int fd, const void* buf, size_t count);
      
      
      /**
       * @param fd      文件描述符
       * @param buf     字符缓冲区,用来储存读取到的字符
       * @param count   读取数量,从文件中读取count个字符到buf中
       * @return int    执行成功时返回读取到的字节数,失败时返回-1
       */
      ssize_t read(int fd, void* buf, size_t count);
      
      
      /**
       * @param fd      文件描述符
       * @param offset  相对于whence的偏移量
       * @param whence  位置宏
       */
      off_t lseek(int fd, off_t offset, int whence);
      

      lseek中的whence

      意义
      SEEK_SET移动光标到文件起始位置
      SEEK_CUR保持当前光标位置不变
      SEEK_END移动光标到文件结尾位置

      lseek函数通过调整文件偏移量(光标位置)可以设置我们从文件的何处开始读取或写入。

    • 演示

      /*
       * name:        20230918_1.cpp
       * funtion: 关闭文件
       * Date:        2023-09-18
       * Author:      Torch-HXM
      */
      
      #include <iostream>
      #include <fcntl.h>
      #include <unistd.h>
      #include <sys/stat.h>
      #include <string.h>
      
      using namespace std;
      
      int main(){
              // 打开文件
              char filePath[] = "./20230918_1.txt";
              int flags = O_CREAT | O_RDWR;
              int fd = open(filePath, flags);
      
              if(fd){
                      cout<< "成功打开文件 "<< filePath<< " 文件描述符为:"<< fd<< endl;
              }
              else{
                      cout<< "open file failed."<< endl;
                      return -1;
              }
      
      
              // 此时文件的内容为空
              char writeBuf[] = "Tody is September 28th, 2023.";
              int writeCount =  strlen(writeBuf);
      
              // 写入内容
              int getSize = write(fd, writeBuf, writeCount);
      
              if(getSize==-1){
                      cout<< "写入内容失败!"<< endl;
                      return -1;
              }
              else{
                      cout<< "'";
                      for(int i=0;i<writeCount;i++){
                              cout<< writeBuf[i];
                      }
                      cout<< "'"<< "写入成功!"<<endl;
              }
      
      
              // 调整文件偏移量至文件开头
              off_t offset = 0;
              int whence = SEEK_SET;
              int where = lseek(fd, offset, whence);
      
              if(where==-1){
                      cout<<"文件偏移量调整失败!"<<endl;
                      return -1;
              }
              else{
                      cout<<"文件偏移量已调整至:"<< where<< endl;
              }
      
              // 读取文件内容
              char readBuf[100];
              int readCount = strlen(writeBuf);
              getSize = read(fd, readBuf, readCount);
      
              if(getSize == -1){
                      cout<< "读取文件失败!"<< endl;
                      return -1;
              }
              else{
                      cout<< "已读取内容:";
                      for(int i=0;i<getSize;i++){
                              cout<< readBuf[i];
                      }
                      cout<< endl;
              }
      
              // 关闭文件
              fd = close(fd);
      
              if(fd==0){
                      cout<< "close file successfully."<< endl;
              }
              else{
                      cout<< "close file failed."<<endl;
                      return -1;
              }
      
              return 0;
      }
      

      执行结果:

      成功打开文件 ./20230918_1.txt 文件描述符为:3
      'Tody is September 28th, 2023.'写入成功!
      文件偏移量已调整至:0
      已读取内容:Tody is September 28th, 2023.
      close file successfully.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值