zoukankan      html  css  js  c++  java
  • 常用的文件IO操作


    学习内容

        1)open函数的flag

      2)linux系统如何管理文件

      3)lseek详解

      4)dup和dup2函数介绍

      5)标准IO库介绍


    如何查man手册:man 1 xx查linux shell命令,man 2 xxx查API, man 3 xxx查库函数

    1、open函数的flag

      大家有没有发现open函数有两个如下,

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

     多了一个mode,查看man手册可以发现作用为O_CREAT创建了新文件时对文件的权限设置,如下图:

    常用的flag:

      1)读写权限:O_RDONLY(只读), O_WRONLY(只写), O_RDWR(可读可写).

      2)打开存在并有内容的文件时:O_APPEND(追加)、O_TRUNC(覆盖)

      3)打开不存在的文件时:O_CREAT(覆盖创建)、O_EXCL(确保文件创建) O_EXCL标志和O_CREAT标志来结合使用,没有文件时创建文件,有这个文件时会报错提醒我们。

      4)阻塞与非阻塞:O_NONBLOCK(非阻塞)我们打开一个文件默认就是阻塞式的,如果你希望以非阻塞的方式打开文件,则flag中要加O_NONBLOCK标志,只用于设备文件,而不用于普通文件。

      5)write非阻塞等待:O_SYNC  无O_SYNC时write只是将内容写入底层缓冲区即可返回,在合适的时候会将buf中的内容一次性的同步到硬盘中。这种设计是为了提升硬件操作的性能和销量,提升硬件寿命;但是有时候我们希望硬件不好等待,直接将我们的内容写入硬盘中,这时候就可以用O_SYNC标志。

    2、linux系统如何管理文件

      1)硬盘中的静态文件和inode(i节点)

      硬盘中可以分为两大区域:一个是硬盘内容管理表项,另一个是真正存储内容的区域。操作系统访问硬盘时是先去读取硬盘内容管理表,从中找到我们要访问的那个文件的扇区级别的信息,然后再用这个信息去查询真正存储内容的区域,最后得到我们要的文件。而这个管理表中以文件为单位记录了各个文件的各种信息,每一个文件有一个信息列表(我们叫inode,i节点,其实质是一个结构体,这个结构体有很多元素,每个元素记录了这个文件的一些信息,其中就包括文件名、文件在硬盘上对应的扇区号、块号那些东西·····)

    实用:大家格式化硬盘(U盘)时发现有:快速格式化和底层格式化。快速格式化非常快,格式化一个32GB的U盘只要1秒钟,普通格式化格式化速度慢。这两个的差异?其实快速格式化就是只删除了U盘中的硬盘内容管理表(其实就是inode),真正存储的内容没有动。这种格式化的内容是有可能被找回的。

      2)内存中被打开的文件和vnode(v节点)

      一个程序的运行就是一个进程,我们在程序中打开的文件就属于某个进程。每个进程都有一个数据结构用来记录这个进程的所有信息(叫进程信息表),表中有一个指针会指向一个文件管理表,文件管理表中记录了当前进程打开的所有文件及其相关信息。文件管理表中用来索引各个打开的文件的index就是文件描述符fd,我们最终找到的就是一个已经被打开的文件的管理结构体vnode。

    3、lseek详解

    off_t lseek(int fd, off_t offset, int whence);

    whence就是代表一个参考位置(用来表示文件开始处到文件当前位置的字节数)offset代表一个偏移量

    参数 offset 的含义取决于参数 whence:
    SEEK_SET,则返回的文件偏移量将被设置为 offset。
    SEEK_CUR,则返回的文件偏移量将被设置为 cfo 加上 offset,
    SEEK_END,则返回的文件偏移量将被设置为文件长度加上 offset,

      1)文件指针:当我们要对一个文件进行读写时,一定需要先打开这个文件,所以我们读写的所有文件都是动态文件。动态文件在内存中的形态就是文件流的形式。

      2)在动态文件中,我们会通过文件指针来表征这个正在操作的位置。所谓文件指针,就是我们文件管理表这个结构体里面的一个指针如上图。所以文件指针其实是vnode中的一个元素。这个指针表示当前我们正在操作文件流的哪个位置。这个指针不能被直接访问,linux系统用lseek函数来访问这个文件指针。

      3)read和write函数都是从当前文件指针处开始操作的,所以当我们用lseek显式的将文件指针移动后,那么再去read/write时就是从移动过后的位置开始的。

      4)用lseek计算文件长度

    eg:return=lseek(fd,0,SEEK_END);//表示从文件末尾

      5)用lseek构建空洞文件,这个文件中有一段是空的

      6)使用实列:

    首先我们看到a.txt里面全都是f,其实我们用O_APPEND就可以在后面追加内容,而我们用lseek来随意位置追加内容

    使用SEEK_END

    lseek(fd,0,SEEK_END);//在write1前使用一次和O_APPEND同等效果,在write之后再使用一次即可的读取出当前文件的长度

    4、dup和dup2函数介绍

    int dup(int oldfd);
    int dup2(int oldfd, int newfd);//返回值即为newfd

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

      2) 进行文件描述符复制:dup并不能指定分配的新的文件描述符的数字,dup2系统调用修复了这个缺陷。dup返回的fd和原来的oldfd都指向oldfd打开的那个【动态文件】,操作这两个fd实际操作的都是oldfd打开的那个文件。实际上构成了文件共享

      3) fd小于3的分别对应于,stdin(0)、stdout(1)、stderr(3),也就是标准输入、标准输出、标准错误。

      4)实现标准输出的重定位实例

    #include<stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include<unistd.h>
    #define FILEPATH    "a.txt"
    int main(int argc,char *argv[])
    {
        int fd=-1;
        int i=-1; //实际向文件中写入的字节数
        int j=-1;//实际向文件中写入的字节数
        int k;//用dup复制后的文件描述符
     
        char b[100]= "bbbbbadfghgdfh";
      //打开一个文件操作
        fd=open(FILEPATH, O_RDWR );
        if(-1==fd)
        {
            printf("文件打开失败!
    ");
            perror("open:");
            return 0;
        }
        else
        {
            printf("文件打开成功!fd=%d
    ",fd);
        }
        close(1);   //关闭标准输出文件描述符1
        k=dup(fd);  //实现重定位
        j=write(k,&b,20);
        if(-1==j)
        {
            printf("写入文件失败
    ");
            perror("写入");
            return 0;
        }
        printf("write succeed!
    ");
        close(fd);
        return 0;
    }

    5、标准IO库介绍

      1)标准IO和文件IO有什么区别:标准IO是C库函数(也是通过调用API来完成操作的),而文件IO是linux系统的API(API类似于一种接口,是由操作系统提供的)

      2)常用标准IO函数介绍:fopen、fclose、fwrite、fread、ffulsh(刷新标准库函数的缓存,直接写进操作系统的缓冲区中)、fseek

      3)一个简单的标准IO读写文件实例

    #include<stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include<unistd.h>
    
    #define FILEPATH    "a.txt"
    
    int  main(int argc,char *argv[])
    {
        FILE *fd = NULL;  //定义一个文件指针
        int ret = 0;
        char Wrtebuff[10]={"sdfgdjdytr"};
        fd=fopen(FILEPATH, "r+");
        if(NULL == fd)
        {
            printf("文件打开失败!
    ");
            perror("open");
            return 0;
        }
        ret = fwrite(Wrtebuff,1,sizeof(Wrtebuff),fd);
        if(ret<0)
        {
            perror("write");
            return 0;
        }
        printf("文件写入成功!
    ");
        
        return 0;
    }

  • 相关阅读:
    【思维导图】前端开发JavaScript-巩固你的JavaScript知识体系
    前端面试日更解答 2020-03-28
    CSS基础知识
    HTML基础知识
    Web页面制作基础
    前端面试日更解答 2020-03-24
    前端面试日更解答 2020-03-23
    前端面试日更解答 2020-03-22
    前端面试日更解答 2020-03-21
    前端面试日更解答 2020-03-20
  • 原文地址:https://www.cnblogs.com/ncne/p/10987690.html
Copyright © 2011-2022 走看看