文件读取

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

$ echo hello > foo
$ ./cat foo
hello

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

精简版 cat 命令源码如下:

cat.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define BUFFER_SIZE 10240

int main(int argc, char *argv[])
{
    // 检查参数
    if (argc != 2) {
        fprintf(stderr, "Usage: cat [filepath]\n");
        exit(1);
    }

    // 打开文件
    int fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("Open file");
	exit(2);
    }

    // 循环输出
    for (;;) {
        // 缓冲区
        char buffer[BUFFER_SIZE];

        // 读文件
        int bytes = read(fd, buffer, BUFFER_SIZE);
        if (bytes == -1) {
            perror("Read file");
	    exit(3);
        }
        else if (bytes == 0) {
            break;
        }

        // 写到标准输出
        // 文件描述符0代表标准输出
        if (write(0, buffer, bytes) == -1) {
            perror("Write stdout");
            exit(4);
        }
    }

    close(fd);

    return 0;
}

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

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

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

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

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

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

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

$ man 2 open

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

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

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

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

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

下一步

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

../../_images/wechat-mp-qrcode.png

小菜学编程