文件读取

本文通过一个精简版 cat 命令的实作,演示如何使用 Linux 系统调用读取文件内容。精简版 cat 命令功能非常单一,但五脏俱全,足以用来演示。其用法如下:

  1. $ echo hello > foo
  2. $ ./cat foo
  3. hello

如上, cat 接受一个命令行参数——被读取文件路径,打开并读取文件,最后将文件内容输出到标准输出。如果文件不存在, cat 命令提示错误并退出。

精简版 cat 命令源码如下:

cat.c

  1. #include <fcntl.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #define BUFFER_SIZE 10240
  6. int main(int argc, char *argv[])
  7. {
  8. // 检查参数
  9. if (argc != 2) {
  10. fprintf(stderr, "Usage: cat [filepath]\n");
  11. exit(1);
  12. }
  13. // 打开文件
  14. int fd = open(argv[1], O_RDONLY);
  15. if (fd == -1) {
  16. perror("Open file");
  17. exit(2);
  18. }
  19. // 循环输出
  20. for (;;) {
  21. // 缓冲区
  22. char buffer[BUFFER_SIZE];
  23. // 读文件
  24. int bytes = read(fd, buffer, BUFFER_SIZE);
  25. if (bytes == -1) {
  26. perror("Read file");
  27. exit(3);
  28. }
  29. else if (bytes == 0) {
  30. break;
  31. }
  32. // 写到标准输出
  33. // 文件描述符0代表标准输出
  34. if (write(0, buffer, bytes) == -1) {
  35. perror("Write stdout");
  36. exit(4);
  37. }
  38. }
  39. close(fd);
  40. return 0;
  41. }

这是一个非常简短的程序,代码逻辑都在 main 函数里。

10-14 行处,先检查命令行参数。如果参数个数不符合预期,输出错误并退出。

17-21 行处,调用 open 系统调用打开文件。open 系统调用返回一个 文件描述符 ,后续可以通过该描述符操作文件。

如果文件打开失败, open 将返回 -1 ,程序需要对异常情况进行处理。导致打开文件的原因又很多,例如:文件不存在;抑或权限不足等等。

注意到,我们以只读模式打开文件—— flags 参数为 O_RDONLYflags 参数用于指定不同的访问模式,可以是以下三者之一:

  • O_RDONLY ,只读模式
  • O_WRONLY ,只写模式
  • O_RDWR ,读写模式

更多关于 open 系统调用使用方法,请参考 open(2) - Linux manual page 。这是一份唾手可得的编程文档:

  1. $ man 2 open

24-44 行,在一个循环内不停读取文件并写到标准输出,直到文件读完。

在调用 read 系统调用前,需要准备一个 缓冲区 ,用于存储文件内容。read 负责将文件内容从磁盘读取到缓冲区中,并返回成功读取字节数。

read 需要传入 3 个参数: 文件描述符缓冲区地址 以及 读取字节数 (受缓冲区限制)。read 返回值为零,意味着文件读取完毕,可以退出循环。

同样,更多关于 read 系统调用使用方法,请参考 read(2) - Linux manual page

46 行,在程序退出前,关闭文件描述符。其实,这个步骤可以省略,因为程序退出时,内核会关闭其所有文件描述符。

下一步

订阅更新,获取更多学习资料,请关注我们的 微信公众号

../../_images/wechat-mp-qrcode.png小菜学编程

参考文献