• 文件IO与标准IO探究及总结


    本质:实现好的一组函数接口
    好处:屏蔽底层细节,向上层提供统一的接口


    Liunx 库 : xxx.so(动态库) , xxx.a(静态库)
    windows 库 : xxx.dll

    问题:所有的库函数都调用了系统调用接口
    回答:不是,例如 strcpy ...

    系统调用:操作系统给用户提供的一组函数接口,通过这一组函数接口可以从用户空间
    进入内核空间,从而使用内核提供的服务

    二 文件操作的系统调用函数接口

    (1)文件打开

    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);
    参数:
    @pathname 需要打开的文件
    @flags
    O_RDONLY ,O_WRONLY ,O_RDWR ,O_CREAT(不存在,则创建) ,O_TRUNC(清空),O_APPEND (追加)
    这些标志可以通过 "|" 一起使用

    echo "hello" > log (O_WRONLY | O_CREAT |O_TRUNC)
    echo "hello" >> log (O_WRONLY | O_CREAT |O_APPEND)

    @mode
    权限,当需要创建文件的(O_CREAT)时候,用来指定文件权限 (八进制表示:0666)
    注意:创建实际权限 mode & ~umask

    666 & ~002 => 664

    返回值:
    成功返回 文件描述符,失败返回-1
    -----------------------------------------------------------------------------------------------------
    文件描述符:它是一个整数,用来标示打开的文件,分配原则:最小未使用,可以使用的文件描述符(0-1023)

    默认被使用文件描述符
    0 : 标准输入 (键盘)
    1 : 标准输出 (终端)
    2 : 标准出错 (终端)

    errno : 全局变量,用来标示错误的原因,它是一个数子值
    strerror(errno) -> 描述错误信息的字符串
    ----------------------------------------------------------------------------------------------------


    <1>以读写方式打开一个文件,文件不存在则创建,文件存在则清空

    如:

    (2)文件读写
    int read(int fd, void *buf, int count);
    功能:读文件
    参数:
    @fd 打开的文件
    @buf 用来存放读取的数据内存首地址
    @count 期望读取的字节数
    返回值:
    成功返回读取字节数,失败返回-1,如果读到文件尾部,返回0

    int write(int fd, void *buf, int count);
    功能:写文件
    参数:
    @fd 打开的文件
    @buf 需要写入数据存放的首地址
    @count 需要写入的字节数
    返回值:
    成功返回写入字节数,失败返回-1

    (3)文件关闭
    int close(int fd);

    如何实现文件拷贝呢

    提示:

    ./a.out src dest

    src : O_RDONLY
    dest: O_WRONLY | O_TRUNC | O_TRUNC

    比较两个文件是否有差异:diff src dest

    (4)文件定位
    含义:修改内核空间文件表项中的offset值

    long lseek(int fd, long offset, int whence);
    参数:
    @fd 打开的文件
    @offset 偏移量 ( 正数 : 向后偏移 , 负数:向前偏移)
    @whence SEEK_SET:文件开头 SEEK_CUR:当前位置 SEEK_END:文件尾部
    返回值:
    成功返回从头开始偏移的字节数(修改之后内核中文件表项中的offset值),失败返回 -1

    lseek(fd,100,SEEK_SET); =>内核中offset = 100
    lseek(fd,100,SEEK_CUR); =>内核中offset = offset + 100
    lseek(fd,0,SEEK_END); =>内核中offset = 文件大小

    输出一个指定文件的大小:

      lseek(fd,0,SEEK_END)

    tip:

    在linux 下vi可以使用:

    ctrl + ] 跳转
    ctrl + t 返回


    三 C标准库提供的文件操作的函数接口

    使用C标准库文件操作函数的好处:
    <1>编写的代码移植性好
    <2>效率较高

    特点:标准C库的函数在操作文件的时候,有一个缓存

    1.打开文件
    流 : FILE * ,打开文件的时候,返回描述打开文件信息(文件描述符 + 缓存的信息)的结构体首地址

    FILE *fopen(const char *path, const char *mode);
    参数:
    @path 需要打开的文件
    @mode
    "r" 只读 -> O_RDONLY
    "r+" 读写 -> O_RDWR

    "w" 只写,文件不存在则创建 ,存在则清空 ->O_WRONLY | O_CREAT | O_TRUNC,0666
    "w+" 读写,文件不存在则创建 ,存在则清空 ->O_RDWR | O_CREAT | O_TRUNC,0666

    "a" 只写,文件不存在则创建 ,存在则追加写 ->O_WRONLY | O_CREAT | O_APPEND,0666
    "a+" 读写,文件不存在则创建 ,存在则追加写 ->O_RDWR | O_CREAT | O_APPEND,0666
    (注意:,没有进行写操作的时候,读操作是从头开始,写操作总是在末尾)

    问题:新建文件权限是多少?
    回答:默认指定的是0666,实际权限:666 & ~umask

    返回值:
    成功返回流指针,失败返回NULL

    -------------------------------------------------------------
    stdin ----> 0
    stdout ----> 1
    stderr ----> 2
    类型: FILE *
    -----------------------------------------------------------

    2.格式化输出

    int printf(const char *format, ...);
    只能向标准输出输出

    int fprintf(FILE *stream, const char *format, ...);
    向指定的流输出

    int sprintf(char *str, const char *format, ...);
    向指定的地址输出

    循环从从终端上输入字符串写到文件中去,如果输入的是"quit",则结束

    3.缓存类型

    <1>全缓存
    满,调用fflush , 程序正常结束(调用了exit函数,或者从main函数返回)
    <2>行缓存
    遇到换行符(' '),满,调用fflush , 程序正常结束(调用了exit函数,或者从main函数返回)
    <3>不缓存
    直接输出,标准出错 stderr

    注意:默认打开的文件,它的缓存是全缓存,当一个流和终端设备关联的时候,它的缓存是行缓存类型(标准输出stdout)

    4.单个字符的文件读写

    int fgetc(FILE *stream);
    功能:从一个流中读取一个字符
    参数:
    @stream 打开的文件
    返回值:
    成功返回读取的一个字符,失败或读到文件尾部-1

    int fputc(int c, FILE *stream);
    功能:将一个字符写入指定的流
    参数:
    @stream 打开的文件
    返回值:
    成功返回写入的一个字符,失败-1

    5. 多个字符的读写

    char *fgets(char *s, int size, FILE *stream);
    功能:从文件中读取多个字符
    参数:
    @s 存放读取的字符
    @size 每次读取大小
    @stream 打开的文件
    返回值:
    成功返回s,读到文件尾部返回NULL,失败返回NULL

    注意:
    <1>每次最多读取size-1个字符
    <2>每次读取结束后,都会自动添加''
    <3>每次读取结束的时刻:
    [1]读到文件尾没有数据可读
    [2]读到' '字符
    [3]已经读取了size-1个字符

    问题:如何使用fgets函数读取从键盘输入的字符串?
    char buf[1024];
    / /输入:abc回车
    fgets(buf,sizeof(buf),stdin);
    // 其实得到:buf:abc
    //去掉回车键
    buf[strlen(buf) - 1] = '';


    int fputs(const char *s, FILE *stream);
    功能:将一个字符串写入一个流,不会写''字符进去(写的是''之前的字符)
    参数:
    @s 字符串
    @stream 打开的文件
    返回值:
    成功返回大于0的数,失败返回-1(EOF)

    注意:
    bug 不能对二进制文件操作


    6.二进制文件读写(按数据元素读写)

    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    功能:从流中读取指定个数的数据元素
    参数:
    @ptr 存放读取的数据
    @size 数据元素的大小
    @nmemb 数据元素的个数
    @stream 打开的文件
    返回值:
    成功返回读取的数据元素个数,失败返回-1 ,读到文件尾部返回0 ,没有数据元素可读

    注意:
    每次读取的字节数: 成功读取的元素个数 * 元素的大小

    size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

    实例:
    struct student{
    char name[15];
    int age;
    int score;
    };

    struct student stu[2] = {{"xiaoming",10,20},{"xiaohong",20,30}};
    将学生信息写入文件中

    然后从文件中读取第一个学生的信息,然后输出。

    7.文件定位

    int fseek(FILE *stream, long offset, int whence);
    功能:实现文件定位,就是在修改内核空间文件表项中的offset
    参数:
    @stream 打开的文件
    @offset 偏移量
    @whence SEEK_SET,SEEK_CUR,SEEK_END
    返回值:
    成功返回0,失败返回-1

    long ftell(FILE *stream); //lseek(fd,0,SEEK_CUR);
    功能:返回文件偏移量的值

    void rewind(FILE *stream);//lseek(fd,0,SEEK_SET);
    功能:定位到文件头

    获取文件大小:

    三 文件IO(系统调用函数接口) 和 标准IO(标准C库提供的函数接口)

    1.标准IO提供的函数接口,绝大部分都是在系统调用之上实现的,它操作对象是流
    2.标准IO带缓存的
    3.使用标准IO代码移植性更好
    API:
    FILE *fopen(const char *pathname,char *mode);
    int fclose(FILE *fp);
    读写:
    int fgetc(FILE *fp) / int fputc(int c,FILE *fp);
    char *fgets(void *s,int size,FILE *fp) / int fputs(char *s,FILE *fp);
    int fread(void *ptr,int size,int nmemb,FILE *fp) / fwrite
    文件定位
    int fseek(FILE *fp,int offset,int whence);
    long ftell(FILE *fp);
    void rewind(FILE *fp);
    ---------------------------------------------------------------------------
    1.文件IO就是系统调用,它不带缓存,它的操作对象文件描述符
    2.文件IO针对Linux操作系统,除了可以操作普通文件之外,还可以对设备文件读写

    打开
    int open(const char *pathname,int flag,int mode);
    读写
    int read(int fd,void *buf,int count);
    int write(int fd,void *buf,int count);
    关闭
    close(int fd);

  • 相关阅读:
    SVG平移和缩放(鼠标滚轮)的实现
    CSS之容器水平垂直居中
    CSS之flex布局
    CSS之鼠标悬停——内容变深/变浅
    CSS之clip-path绘制多边形
    axios
    .Net 反射
    Redis
    .Net Core GRPC报错
    Python 京东云无线宝消息推送
  • 原文地址:https://www.cnblogs.com/bwbfight/p/9263134.html
Copyright © 2020-2023  润新知