• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Linux从入门到精通文件I/O操作C语言vs系统调用

武飞扬头像
Ggggggtm
帮助1

学新通

文章目录

一、C语言的文件IO相关函数操作

1、1 fopen与fclose

1、2 fwrite

1、3 fprintf与fscanf

1、4 fgets与fputs

二、系统调用相关接口

2、1 open与close

2、2 write和read

三、简易模拟实现cat指令

四、总结


👀 专栏:Linux从入门到精通  👀

💥 标题:文件操作💥

 ❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️ 

一、C语言的文件IO相关函数操作

1、1 fopen与fclose

  fopen() 函数原型:FILE *fopen(const char *filename, const char *mode); 作用:打开一个文件,并返回一个文件指针。 参数:

  • filename:要打开的文件名(含路径)
  • mode:打开文件的模式,如 "r" 表示只读,"w" 表示写入(如果文件存在则清空内容),"a" 表示追加写入等。

  具体如下图:

学新通

  fclose() 函数原型:int fclose(FILE *stream); 作用:关闭一个打开的文件。 参数:

  • stream:要关闭的文件指针

   具体可结合下图理解:学新通

  我们知道使用 fopen 时需要添加索要打开文件的路径。当我们以w的方式进行打开时,该文件不存在会自动创建文件,具体代码如下图:

  1.  
    #include<stdio.h>
  2.  
     
  3.  
    int main()
  4.  
    {
  5.  
    FILE* fd = fopen("log.txt","w");
  6.  
    if(fd==NULL)
  7.  
    {
  8.  
    perror("fopen");
  9.  
    }
  10.  
     
  11.  
    fclose(fd);
  12.  
     
  13.  
    //while死循环完全是为了方便查看和观察
  14.  
    while(1)
  15.  
    {
  16.  
    sleep(1);
  17.  
    }
  18.  
    return 0;
  19.  
    }
学新通

  上述代码中,fopen中并没有添加路径,只有一个文件名字。那么能打开成功吗?其次是,当前目录下并没有 log.txt 文件,如果能打开成功,文件会被创建到哪里呢?我们带着这些疑问接着往下看。

  我们不妨先观察一下运行结果,如下图:

学新通  我们看到确实能够出创建出来,也是创建在了当前目录了!这是为什么呢?当一个程序运行起来后,会在内存中创建相应的数据结构,同时变成进程。该进程包含了当前所在的工作目录,且还有当前的可执行文件所在的目录。具体如下图:

学新通  所以即使我们并没有添加路径,操作系统也会知道当前所在的路径。并且默认创建到当前的工作路径下。

1、2 fwrite

  fwrite的函数原型:size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream)。用于将数据块按字节写入文件。参数:

  • ptr:指向要写入的数据的指针。
  • size:要写入的每个数据项的字节数。
  • count:要写入的数据项的数量。
  • stream:目标文件的指针。

  我们也可看下图理解:

  我们现在用fwrite向log.txt中写入,具体代码如下:

  1.  
    #include<stdio.h>
  2.  
    #include<string.h>
  3.  
     
  4.  
    int main()
  5.  
    {
  6.  
    FILE* fp = fopen("log.txt","w");
  7.  
    if(fp==NULL)
  8.  
    {
  9.  
    perror("fopen");
  10.  
    }
  11.  
    //进行文件操作
  12.  
    const char *s1 = "hello Linux\n";
  13.  
    fwrite(s1, strlen(s1), 1, fp);
  14.  
     
  15.  
     
  16.  
    fclose(fp);
  17.  
    return 0;
  18.  
    }
学新通

   这里有一个问题:在写入文件时,要不要把s1的‘\0’写入呢?答案是不用。字符串结尾标志'\0'只是C语言规定的。文件并不用遵守C语言的规则!我们看运行结果:

学新通

  log.txt文件中确实被写入了。假如我们注释掉写入的代码,只是打开后直接关闭文件,结果会是什么呢?如下图:

学新通

  文件内容为空了!!!为什么呢?原因是以“w”的方式打开文件,就是在写入前会被清空文件内容。 

1、3 fprintf与fscanf

  fprintf() 函数原型:int fprintf(FILE *stream, const char *format, ...); 作用:向文件中按指定格式写入数据。 参数:

  • stream:要写入的文件指针。
  • format:格式化字符串,类似于printf()函数的格式化参数。 返回值:成功写入的字符数,出错时返回负值。

  fprintf与printf相比,fprintf第一个参数是FILE* ,其他的都一样。只不过是输出到了指定的文件上。

  fscanf() 函数原型:int fscanf(FILE *stream, const char *format, ...); 作用:从文件中按指定格式读取数据。 参数:

  • stream:要读取的文件指针。
  • format:格式化字符串,类似于scanf()函数的格式化参数。 返回值:成功读取并匹配的项目数量,出错或到达文件结尾时返回EOF。

  fscanf() 与scanf() 相比,fscanf() 第一个参数是FILE* ,其他的都一样。读取数据时,是从指定的文件上读取。

  这里我们就举例说明fsacnf,我们想要把文件的内容读取并输出,代码如下:

  1.  
    #include<stdio.h>
  2.  
    #include<unistd.h>
  3.  
    #include<string.h>
  4.  
     
  5.  
    int main()
  6.  
    {
  7.  
    FILE* fp = fopen("log.txt","r");
  8.  
    if(fp==NULL)
  9.  
    {
  10.  
    perror("fopen");
  11.  
    }
  12.  
     
  13.  
    char line[128];
  14.  
    while(fscanf(fp,"%s",line) != EOF)
  15.  
    {
  16.  
    printf("%s\n", line);
  17.  
    }
  18.  
    return 0;
  19.  
    }
学新通

  运行结果如下:

学新通

  注意,当我们要读取内容是,要修改打开文件的方式,应该以“r”的方式打开文件。否则会出现意想不到的结果!!!

1、4 fgets与fputs

  fgets() 函数原型:char *fgets(char *str, int n, FILE *stream); 作用:从文件中读取一行字符串。 参数:

  • str:要读取的字符串存放的缓冲区。
  • n:最多读取的字符数(包括换行符)。
  • stream:要读取的文件指针。 返回值:成功时返回str,失败或到达文件结尾时返回NULL。

   具体如下图:

学新通

  fputs() 函数原型:int fputs(const char *str, FILE *stream); 作用:向文件中写入一个字符串。 参数:

  • str:要写入的字符串。
  • stream:要写入的文件指针。 返回值:成功写入的字符数,出错时返回EOF。

 具体如下图:

学新通

  我们结合下述实例来理解fgets和fputs的用法,代码如下:

  1.  
    #include<stdio.h>
  2.  
    #include<unistd.h>
  3.  
    #include<string.h>
  4.  
     
  5.  
    int main()
  6.  
    {
  7.  
    FILE* fp = fopen("log.txt","r");
  8.  
    if(fp==NULL)
  9.  
    {
  10.  
    perror("fopen");
  11.  
    }
  12.  
    char line[64];
  13.  
    //fgets -> C -> s(string) -> 会自动在字符结尾添加\0
  14.  
    while(fgets(line, sizeof(line), fp) != NULL)
  15.  
    {
  16.  
    //printf("%s", line);
  17.  
    fputs(line, stdout); //输出到屏幕上
  18.  
    }
  19.  
    fclose(fp);
  20.  
    return 0;
  21.  
    }
  22.  
     
  23.  
学新通

   运行结果如下:学新通

二、系统调用相关接口

2、1 open与close

   open()函数:open函数用于打开文件并获取文件描述符。它接受一个文件路径和一组标志作为参数,并返回一个用于后续文件操作的文件描述符。函数原型:int open(const char *pathname, int flags, mode_t mode)。详细解释:

  • 函数说明:打开文件并获取文件描述符。
  • 参数:
    • pathname:要打开的文件路径。
    • flags:打开文件的标志,例如O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)等。
    • mode:在创建新文件时使用的权限位。
  • 返回值:
    • 成功:返回一个非负整数,表示文件描述符
    • 失败:返回-1,并设置errno来指示错误。

  具体可结合下图理解:

学新通

学新通

  有很多的选项,我们想要添加那个选项,只需要在第二个参数 按位与(‘|’) 上就行。为什么 按位与(‘|’)呢?这里涉及到了位图的知识。想要知道的可以去了解一下位图。

  close()函数:close函数用于关闭打开的文件。它接受文件描述符作为参数,并返回一个表示成功与否的状态值。   函数原型:int close(int fd)。详细解释:

  • 函数说明:关闭打开的文件。
  • 参数:
    • fd:要关闭的文件描述符。
  • 返回值:
    • 成功:返回0。
    • 失败:返回-1,并设置errno来指示错误。

  我们通过如下实例来理解open和close。代码如下:

  1.  
    #include<stdio.h>
  2.  
    #include<unistd.h>
  3.  
    #include<string.h>
  4.  
    #include<sys/types.h>
  5.  
    #include<sys/stat.h>
  6.  
    #include<fcntl.h>
  7.  
     
  8.  
     
  9.  
    int main()
  10.  
    {
  11.  
    int fd1 = open("log.txt", O_RDONLY);
  12.  
    int fd2 = open("log2.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666); //rw-rw-rw-
  13.  
    if(fd1 < 0 || fd2 < 0)
  14.  
    {
  15.  
    perror("open");
  16.  
    return 1;
  17.  
    }
  18.  
    close(fd1);
  19.  
    close(fd2);
  20.  
     
  21.  
    return 0;
  22.  
    }
学新通

  运行结果如下:

学新通

  确实打开成功了。为什么log2.txt与我们设置的权限并不相同呢?不要忘记了系统中还有umask掩码。

学新通

2、2 write和read

  write()函数:write函数用于向文件中写入数据。它接受文件描述符、要写入的数据和字节数作为参数,并返回实际写入的字节数。函数原型:ssize_t write(int fd, const void *buf, size_t count)。详细解释:

  • 函数说明:向文件中写入数据。
  • 参数:
    • fd:要写入的文件描述符。
    • buf:要写入的数据的缓冲区。
    • count:要写入的字节数。
  • 返回值:
    • 成功:返回实际写入的字节数。
    • 失败:返回-1,并设置errno来指示错误。

  read()函数:read函数从文件中读取数据。它接受文件描述符、缓冲区指针和要读取的字节数作为参数,并返回实际读取的字节数。函数原型:ssize_t read(int fd, void *buf, size_t count)。详细解释:

  • 函数说明:从文件中读取数据。
  • 参数:
    • fd:要读取的文件描述符。
    • buf:用于存储读取数据的缓冲区。
    • count:要读取的最大字节数。
  • 返回值:
    • 成功:返回实际读取的字节数。
    • 失败:返回-1,并设置errno来指示错误。

  write和read用起来也相对简单。这里就不再举例详细解释说明了。 

三、简易模拟实现cat指令

  我们知道,cat是打印出一个文件的内容。我们学习了文件操作后,就来简单的模拟实现一下cat指令。

  cat指令不就是接受到文件,然后打印出文件的内容吗。这好像就是我们刚刚学了文件操作。我们直接看代码:

  1.  
    #include<stdio.h>
  2.  
    #include<unistd.h>
  3.  
    #include<string.h>
  4.  
    int main(int argc,char* argv[])
  5.  
    {
  6.  
    if(argc != 2)
  7.  
    {
  8.  
    printf("argv error!\n");
  9.  
    return 1;
  10.  
    }
  11.  
    FILE *fp = fopen(argv[1], "r");
  12.  
    if(fp == NULL)
  13.  
    {
  14.  
    //strerror
  15.  
    perror("fopen");
  16.  
    return 2;
  17.  
    }
  18.  
     
  19.  
    //按行读取
  20.  
    char line[64];
  21.  
    //fgets -> C -> s(string) -> 会自动在字符结尾添加\0
  22.  
    // 将文件的内容打印到屏幕上
  23.  
    while(fgets(line, sizeof(line), fp) != NULL)
  24.  
    {
  25.  
    //printf("%s", line);
  26.  
    fprintf(stdout, "%s", line); //fprintf->stdout?
  27.  
    }
  28.  
    fclose(fp);
  29.  
    return 0;
  30.  
    }
学新通

  在运行时加上文件名就可以打印出文件的内容了。再加上我们之前讲到的创建子进程进行程序替换实现的简易版的shell,不就是实现了cat指令嘛!!!我们看输出结果:

学新通

四、总结

  每套语言都是有自己的文件操作函数,底层都是封装的系统调用的接口。但是操作系统不只是有Linux,还有windows等等。那语言就是封装所有的操作系统的接口呗。只不过是在调用时会有选择判断。这样封装后,语言就有了跨平台性。

  上文中有一个名词:文件描述符。我们并没有对此进行详解。下篇文章会对此进行讲解。这个也是一个重点!!!

  本片文章的讲解就到这里。感谢阅读ovo~ 

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfkkhh
系列文章
更多 icon
同类精品
更多 icon
继续加载