常用的文件IO操作

Posted ncne

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常用的文件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("文件打开失败!\\n");
        perror("open:");
        return 0;
    
    else
    
        printf("文件打开成功!fd=%d\\n",fd);
    
    close(1);   //关闭标准输出文件描述符1
    k=dup(fd);  //实现重定位
    j=write(k,&b,20);
    if(-1==j)
    
        printf("写入文件失败\\n");
        perror("写入");
        return 0;
    
    printf("write succeed!\\n");
    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("文件打开失败!\\n");
        perror("open");
        return 0;
    
    ret = fwrite(Wrtebuff,1,sizeof(Wrtebuff),fd);
    if(ret<0)
    
        perror("write");
        return 0;
    
    printf("文件写入成功!\\n");
    
    return 0;

技术图片

 

以上是关于常用的文件IO操作的主要内容,如果未能解决你的问题,请参考以下文章

常用python日期日志获取内容循环的代码片段

记录C#常用的代码片段

Java一文认识IO操作流

Java一文认识IO操作流

提效小技巧——记录那些不常用的代码片段

Java中Io流操作-File类的常用操作-创建文件,创建文件夹