• C基础之-文件基础


    文件基础

    概念:

      一组相关数据的有序集合

    文件类型

    不同操作系统支持的文件类型是不相同的,这里列出的是linux

    • 常规文件  r
    • 目录文件  d
    • 字符设备文件  c
    • 块设备文件  b
    • 管道文件  p
    • 套接字文件  s
    • 符号链接文件  l

    标准I/O

      由ANSI C标准定义

      主流操作系统上都实现了C库

      特点: 标准I/O通过缓冲机制减少系统调用,实现更高的效率

    FILE

      标准IO用一个结构体类型来存放打开文件的相关信息

      标准IO的所有操作都是围绕FILE来进行的

    流(stream)

      FILE又被称为流(stream)又被分为文本流、二进制流两种

    windows:

      二进制流:换行符 -- ' '

      文本流:换行符 -- ' '' '

    linux

      换行符:' '

    流的缓冲类型

      全缓冲:

        当流的缓冲区无数据或空间时才执行实际I/O操作

      行缓冲:

        当在输入和输出中遇到换行符时,才执行I/O操作

        当流和一个终端关联时,典型的行缓冲

      无缓冲:

        数据直接写入文件,流不进行缓冲

    标准I/O预定的3个流,程序运行时自动打开

    标准输入流 0 STDIN_FILENO stdin
    标准输出流 1 STDOUT_FILENO stdout
    标准错误流 2 STDERR_FILENO stderr

    流打开

    FILE *fopen(const char * path,const char *mode)

    成功时返回流指针;错误返回NULL

    - 当用fopen新建文件后,该文件的权限为0666(rw-rw-rw-)

    - 在linux系统中 umask设定会影响文件的权限访问 可以通过umask函数进行对应的修改

    mode参数:

    "r"或"rb" 以只读的方式打开文件,文件必须存在
    "r+"或"r+b" 以读写的方式打开文件,文件必须存在
    "w"或"wb" 以只写的方式打开文件,若文件存在则文件长度清零。若文件不存在则创建
    "w+"或"w+b" 以只写方式打开文件,其他同"w"
    "a"或"ab" 以只写的方式打开文件,若文件不存在则创建;向文件写入的数据将被追加到文件末尾
    "a+"或"a+b" 以读写的方式打开文件。其他同"a"

    *当给"b"参数时候,表示以二进制方式打开文件,但Linux下忽略该参数

    错误流处理

    void  perror(const char *)

    输入提示关键字 捕获当前错误并加输入的关键字返回

    打印错误信息 例如

    int main() {
       FILE *fp;
       if ((fp = fopen("test","r+"))==NULL){
           perror("fopen");
           return -1;
       }
    }
    
    // out
    // fopen: No such file or directory 
    #include  <stdio.h>
    #include <string.h> // 需要引入
    #include <errno.h> // 需要引入 标准错误输入
    
    
    int main() {
       FILE *fp;
       if ((fp = fopen("test","r+"))==NULL){
           printf("fopen:%s",strerror(errno));
           return -1;
       }
    }

    判断是否出错或结束

    #include <stdio.h>
    int ferror(FILE *stream);
    int feof(FILE *stream);
    
    // ferror()返回1表示流出错;否则返回0
    // feof()返回1表示已经到末尾,否则返回0

    读写流

    流支持不同的读写方式

    读写一个字符:

    // fgetc()/fputc() 一次读/写一个字符

    输入

    #include <stdio.h>
    int fgetc(FILE *stream);
    int getc(FILE *stream);
    int getchar(void);
    
    // 成功时返回读取的字符
    // 若到文件的末尾或出错返回EOF

    Demo:

    #include  <stdio.h>
    
    int main() {
        int ch;
        ch = fgetc(stdin); // 从标准输入获取
        printf("%c", ch);
    }
    #include  <stdio.h>
    
    int main() {
        FILE *fp;
        int ch = 0;
        int i = 0;
        int count = 0;
        if ((fp = fopen("./a.out", "r")) == NULL) {
            perror("fopen");
            return -1;
        }
        while ((ch = fgetc(fp)) != EOF) {
            count++;
        }
        printf("文本长度 :%d", count);
    }

    输出

    #include <stdio.h>
    int fputc(int c,FILE *stream);
    int putc(int c,FILE *stream);
    int putchar(int c);
    
    // 成功时返回写入的字符
    // 出错时返回EOF

    Demo:

    #include  <stdio.h>
    
    int main() {
        putc('a',stdout);
        putchar('
    ');
    }
    
    // out
    // a
    // 
    #include  <stdio.h>
    
    int main() {
        FILE *fp;
        int ch = 0;
    
        if ((fp = fopen("./a.out", "w")) == NULL) {
            perror("fopen");
            return -1;
        }
        for (ch = 'a'; ch <= 'z', ch++;) {
            fputc(ch,fp); // 输出到文本
        }
    }

    文件copy

    #include  <stdio.h>
    
    int main(int argc, char *argv[]) {
        FILE *src, *desc;
        int i;
        if (argc < 3) {
            puts("Few parameters:project : <src> <desc>");
            return -1;
        }
        // 打开目标文件
        if ((src = fopen(argv[1], "r")) == NULL) {
            perror("src:");
            return -1;
        }
        if ((desc = fopen(argv[2], "w")) == NULL) {
            perror("desc:");
            fclose(src); // 失败释放打开的句柄
            return -1;
        }
        while ((i = fgetc(src)) != EOF) {
            fputc(i, desc);
        }
        fclose(src);
        fclose(desc);
    }

    读写一行:

    // fgets()/fputs() 一次读/写一行

    输入

    #include <stdio.h>
    char *gets(char *s);
    char *fgets(char *s,int size,FILE *stream);
    
    // 成功时返回s,到文件末尾或出错时返回NULL
    // gets不推荐使用,容易造成缓冲区溢出;
    // ***遇到'
    '或已输入size-1个字符返回时,总是包含''

    输出

    #include <stdio.h>
    int puts(const char *s);
    int fputs(const char *s,FILE *stream);
    
    // 成功时返回输出的字符个数
    // 出错返回EOF
    // puts将缓冲区s中的字符串输出到stdout,并追加'
    '

    demo:

    #include  <stdio.h>
    
    int main(int argc, char *argv[]) {
        FILE *fp;
        char buf[] = "hello world";
        if ((fp = fopen(argv[1], "a")) == NULL) {
            perror("fopen");
            return -1;
        }
        fputs(buf, fp);
    }

    读写若干个对象:

    // fread()/fwrite() 每次读/写若干个对象,而每个对象具有相同的长度
    #include <stdio.h>
    size_t fread(void *ptr,size_t size,size_t n,FILE *fp); // 容器指针,存储元素的长度,读取的长度,文件句柄
    size_t fwrite(const void *prt,size_t size,size_t n,FILE *fp);
    
    // 成功返回读写的对象个数
    // 出错返回EOF

    demo

    #include  <stdio.h>
    
    int main(int argc, char *argv[]) {
        FILE *fp;
        int buf[10] = {};
        if ((fp = fopen(argv[1], "a")) == NULL) {
            perror("fopen");
            return -1;
        }
        if (fread(buf, sizeof(int), 10, fp) < 0) {
            perror("fread");
            return -1;
        }
    }

    copy文件

    #include  <stdio.h>
    
    #define N 1024
    
    int main(int argc, char *argv[]) {
        FILE *src, *desc;
        int i[N] = {};
        int n = 0;
        if (argc < 3) {
            puts("Few parameters:project : <src> <desc>");
            return -1;
        }
        // 打开目标文件
        if ((src = fopen(argv[1], "r")) == NULL) {
            perror("src:");
            return -1;
        }
        if ((desc = fopen(argv[2], "w")) == NULL) {
            perror("desc:");
            fclose(src); // 失败释放打开的句柄
            return -1;
        }
        if ((n = fread(i, sizeof(int), N, src)) > 0) {
            fwrite(i, n, sizeof(int), desc);
        }
        fclose(src);
        fclose(desc);
    }

    刷新流 

      fflush()

    #include <stdio.h>
    int fflush(FILE *fp);
    
    // 成功时返回0
    // 出错返回EOF

    刷新会将缓冲区的内容落盘

    定位流

      ftell/fseek/rewind

    #include <stdio.h>
    long ftell(FILE *stream); 
    long fseek(FILE *stram,long offset,int whence);  // offset表示偏移量 可正可负  whence表示基准点
    void rewind(FILE *stram);
    
    // ftell()成功时返回流的当前读写位置,错误时返回EOF
    // fseek()定位一个流,成功时返回0,出错时返回EOF
    // whence参数:SEEK_SET-文件开头/SEEK_CUR-当前位置/SEEK_END-文件结尾
    //  rewind将流定位到起始位置
    // 读写流时,当前读写位置自动后移

    Demo:

    在文件末尾增加字符't'

    #include  <stdio.h>
    
    int main(int argc, char *argv[]) {
        FILE *fp;
        // 打开目标文件
        if ((fp = fopen(argv[1], "r")) == NULL) {
            perror("src:");
            return -1;
        }
        // 移动到文章末尾
        fseek(fp, 0, SEEK_END);
        // 增加字符t
        fputc('t', fp);
        fclose(fp);
    }

    获取文件长度

    #include  <stdio.h>
    
    int main(int argc, char *argv[]) {
        FILE *fp;
        int i;
        // 打开目标文件
        if ((fp = fopen(argv[1], "r")) == NULL) {
            perror("src:");
            return -1;
        }
        // 移动到文章末尾
        fseek(fp, 0, SEEK_END);
        // 获取文件长度
        i = ftell(fp);
        printf("长度为%d",i);
        fclose(fp);
    }

    格式化输出

    #include <stdio.h>
    int printf(const char *fmt,...);
    int fprintf(FILE *stream,const char *fmt,...)
    int sprintf(char *s,const char *fmt,...)
    
    // 成功时返回输出的字符个数
    // 出错时返回EOF
    #include  <stdio.h>
    
    int main(int argc, char *argv[]) {
        FILE *fp;
        int year, month, day;
        char buf[64];
        // 打开目标文件
        if ((fp = fopen(argv[1], "r")) == NULL) {
            perror("src:");
            return -1;
        }
        year = 2020;
        month = 05;
        day = 05;
        // 输入到fp的流中
        fprintf(fp, "%d-%d-%d", year, month, day);
        // 输入到缓冲区中
        sprintf(buf, "%d-%d-%d", year, month, day);
    }

    关闭流

    int fclose(FILE *stream);

    调用成功返回0,失败返回EOF,并设置errno

    流成功关闭后会自动刷新缓冲中的数据并释放缓冲区

    当一个程序正常终止时,所有打开的流都会被关闭

    流一旦关闭将不能对其执行任何操作

    在linux中打开流的最大个数(修改ulimit可更改)

    1021 + stdin + stdout + stderr = 1024

    文件I/O

      POSIX(可移植操作系统接口)定义的一组函数

      不提供缓冲机制,每次读写操作都引起系统调用

      核心概念是文件描述符

      可以访问各种类型文件

      在Linux下,标准IO基于文件IO实现

    文件描述符

      每一个打开的文件都对应一个文件描述符

      文件描述符是一个非负整数。Linux为程序中每打开的问你件分配一个文件描述符

      文件描述符从0开始分配,一次递增

      文件IO操作通过文件描述符来完成

      

    文件描述符的0,1,2的含义

      分别是标准输入,标准输出,标准错误 一一对应

    打开文件、创建文件

    #include <fcntl.h>
    int open(const char *path,int oflag,mode_t mode);
    
    // path文件路径
    // oflag打开方式,可以是多个参数
    // 当open成功时返回文件描述符
    // 出错返回EOF
    // 创建文件时第三个参数指定文件的权限
    // 设备文件只能打开不能创建

    O_RDONLY 以只读方式打开文件
    O_WRONLY 以只写方式打开文件
    O_RDWR 以可读写方式打开文件. 上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
    O_CREAT 若欲打开的文件不存在则自动建立该文件.
    O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
    O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
    O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
    O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
    O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
    O_NDELAY 同O_NONBLOCK.
    O_SYNC 以同步的方式打开文件.
    O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
    O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。注:此为Linux2. 2 以后特有的旗标, 以避免一些系统安全问题. 

    参数mode 则有下列数种组合, 只有在建立新文件时才会生效, 此外真正建文件时的权限会受到umask 值所影响, 因此该文件权限应该为 (mode-umaks).
    S_IRWXU00700 权限, 代表该文件所有者具有可读、可写及可执行的权限.
    S_IRUSR 或S_IREAD, 00400 权限, 代表该文件所有者具有可读取的权限.
    S_IWUSR 或S_IWRITE, 00200 权限, 代表该文件所有者具有可写入的权限.
    S_IXUSR 或S_IEXEC, 00100 权限, 代表该文件所有者具有可执行的权限.
    S_IRWXG 00070 权限, 代表该文件用户组具有可读、可写及可执行的权限.
    S_IRGRP 00040 权限, 代表该文件用户组具有可读的权限.
    S_IWGRP 00020 权限, 代表该文件用户组具有可写入的权限.
    S_IXGRP 00010 权限, 代表该文件用户组具有可执行的权限.
    S_IRWXO 00007 权限, 代表其他用户具有可读、可写及可执行的权限.
    S_IROTH 00004 权限, 代表其他用户具有可读的权限
    S_IWOTH 00002 权限, 代表其他用户具有可写入的权限.
    S_IXOTH 00001 权限, 代表其他用户具有可执行的权限.

    Demo:

    #include  <stdio.h>
    #include <fcntl.h>
    
    // 以只写的方式打开文件,如果不存在则创建,如果存在则清空
    int main(int argc, char *argv[]) {
        int fd;
        if ((fd = open("1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
            perror("open");
        }
    }
    #include  <stdio.h>
    #include <fcntl.h>
    #include <string.h> // 需要引入
    #include <errno.h> // 需要引入 标准错误输入
    
    // 以读写的方式打开文件,如果不存在则创建,如果存在则报错
    int main(int argc, char *argv[]) {
        int fd;
        if ((fd = open("1.txt", O_RDWR | O_CREAT, 0666)) < 0) {
            if (errno == EEXIST) {
                perror("exist error");
            } else {
                perror("other error");
            }
        }
    }

    读取文件

    #include <unistd.h>
    ssize_t read(int fd,void *buf,size_t count); // buf容器 count 一般定为缓冲区的大小 保证缓冲区不会溢出
    
    // 成功返回实际读取的字节数
    // 失败返回EOF
    // 读到文件尾时返回0

    demo:统计文件大小

    #include  <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[]) {
        int fd, n, total;
        char buf[64];
        if ((fd = open(argv[1], O_RDONLY)) < 0) {
            perror("open");
            return -1;
        }
        // 读取文件的内容
        // 获取文件大小
        while ((n = read(fd, buf, 64)) > 0) {
            total += n;
        }
    }

    写入文件

    #include <unistd.h>
    ssize_t write(int fd,void *buf,size_t count);
    
    // 成功时返回实际写入的字节数
    // 出错返回EOF
    // count不应超过buf大小

    demo:键盘输入

    #include  <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    
    int main(int argc, char *argv[]) {
        int fd;
        char buf[64];
        if ((fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
            perror("open");
            return -1;
        }
        // 读取文件的内容
        // 获取文件大小
        while (fgets(buf, 20, stdin) > 0) {
            if (strcmp(buf, "quit
    ") == 0) {
                break;
            }
            write(fd, buf, strlen(buf));
        }
    }

    demo:文件的复制

    #include  <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    #define N 64
    
    int main(int argc, char *argv[]) {
        int src, desc;
        int n;
        char buf[N];
        if (argc < 3) {
            perror("Parameter less than 3");
            return -1;
        }
        // 打开源文件 只读
        if ((src = open(argv[1], O_RDONLY)) < 0) {
            perror("open src");
            return -1;
        }
        // 打开目标文件
        if ((desc = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) {
            perror("open desc");
            close(src);
            return -1;
        }
    
        // 获取文件大小
        while ((n = read(src, buf, N)) > 0) {
            write(desc, buf, n);
        }
        close(src);
        close(desc);
    }

    定位

    #include <unistd.h>
    off_t lseek(int fd,off_t offset,int whence); // 与fseek类似 whence基准点一样
    
    // 成功返回当前文件的读写位置
    // 出错返回EOF

    文件关闭

    #include <unistd.h>
    int close(int fd);
    
    // 成功时返回0
    // 出错时返回EOF
    // 程序结束时自动关闭所有打开的文件
    // 文件关闭后,就不能对文件描述符进行操作

    访问(打开)目录

    #include <dirent.h>
    DIR *opendir(const char*name); // name目录路径
    
    // DIR是用来描述一个打开的目录文件的结构体类型
    // 成功时返回目录流指针
    // 错误时返回NULL

    查看目录内容

    #include <dirent.h>
    struct dirent *readdir(DIR *dirp); 
    // struct dirent是用来描述目录流中一个目录项的结构体类型
    // 包含成员 char d_name[256]  —— 文件名  其他参考帮助文档
    // 成功时返回目录流dirp中下一个目录项
    // 出错或到末尾是返回NULL

    demo:查看指定目录下的文件名

    #include  <stdio.h>
    #include <dirent.h>
    
    int main(int argc, char *argv[]) {
        DIR *dirp;
        struct dirent *dp;
        if (argc < 2) {
            perror("Parameter is less than 2");
            return -1;
        }
        // 打开目录
        if ((dirp = opendir(argv[1])) == NULL) {
            perror("opendir");
            return -1;
        }
        while ((dp = readdir(dirp)) != NULL) {
            printf("%s
    ", dp->d_name);
        }
        closedir(dirp);
    }

    关闭目录流

    #include <dirent.h>
    int closedir(DIR *dirp);
    
    // 成功返回0
    // 失败返回-1

    修改文件的访问权限 chmod/fchmod

    #include <sys/stat.h>
    int chomd(const char *path,mode_t mode);
    int fchmod(int fd,mode_t mode);
    
    // mode 例如 0666 权限码
    // 成功时返回0
    // 出错返回EOF
    // root用户和所有者才可以修改文件的访问权限
    // demo:
    chomd("text.txt",0666);

    获取文件的属性

    #include <sys/stat.h>
    int stat(const char *path,struct stat *buf); //path为路径,buf为获取存放的结构体
    int lstat(const char *path,struct stat *buf);
    int fstat(int fd,struct stat*buf);
    
    // stat与lstat区别: 如果path是符号链接stat获取到的是目标文件的属性,而lstat获取的是链接文件的属性
    // 成功时返回0
    // 出错返回EOF
    // struct stat结构体属性
    // mode_t st_mode  文件类型和访问权限
    // uid_t st_uid 所有者id
    // uid_t st_gid 用户组id
    // off_t st_size 文件大小
    // time_t st_mtime 最后修改时间
    // ....

    mode_t st_mode  判断文件类型

    // 通过系统提供的宏来判断文件类型
    st_mode & 0170000
    S_ISREG(st_mode) 0100000
    S_ISDIR(st_mode) 0040000
    S_ISCHR(st_mode) 0020000
    S_ISBLK(st_mode) 0060000
    S_ISFIFO(st_mode) 0010000
    S_ISLNG(st_mode) 0120000
    S_ISSOCK(st_mode) 0140000

    mode_t st_mode 获取权限

    // 通过系统提供的宏来获取we年访问权限
    // 所有者
    S_IRUSR——00400——8 可读
    S_IWUSR——00200——7 可写
    S_IXUSR——00100——6
    // 同组用户
    S_IRGRP——00040——5
    S_IWGRP——00020——4
    S_IXGRP——00010——3
    // 其他组用户
    S_IROTH——00004——2
    S_IWOTH——00002——1
    S_IXOTH——00001——0
    // 通过系统提供的宏来获取文件的访问权限
    // 所有者
    S_IRUSR——00400——8 可读
    S_IWUSR——00200——7 可写
    S_IXUSR——00100——6
    // 同组用户
    S_IRGRP——00040——5
    S_IWGRP——00020——4
    S_IXGRP——00010——3
    // 其他组用户
    S_IROTH——00004——2
    S_IWOTH——00002——1
    S_IXOTH——00001——0
    Songzhibin
  • 相关阅读:
    【笔记】:sort排序大法
    【模块】:torndb
    【模块】:CSV文件 数据可视化
    【模块】:Pygal 绘制直方图
    【模块】:matplotlib 绘制折线图
    【Django】:重构Admin
    【数据结构】:基础
    【数据结构】:排序练习
    【数据结构】:算法(二)
    【数据结构】:算法(一)
  • 原文地址:https://www.cnblogs.com/binHome/p/12840514.html
Copyright © 2020-2023  润新知