`read`系统调用示例
read
。这个函数是与 open
和 write
配合使用,用于从文件描述符指向的文件中读取数据。
1. 函数介绍
read
是一个 Linux 系统调用,它的主要作用是从指定的文件描述符(file descriptor)所关联的文件、管道、套接字等输入流中读取数据,并将其存放到程序提供的缓冲区中。你可以把它想象成从一个“数据源”里“拿出”一些字节放到你自己的“篮子”(缓冲区)里。
2. 函数原型
#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
3. 功能
尝试从文件描述符 fd
指向的文件或资源中读取最多 count
个字节的数据,并将这些数据存储到从 buf
指向的内存地址开始的缓冲区中。
4. 参数
int fd
: 这是文件描述符 (file descriptor)。它是一个非负整数,由open
、creat
、socket
等系统调用成功执行后返回,用以标识一个已打开的文件或资源。void *buf
: 这是一个指向缓冲区的指针。读取到的数据将被存放到从这个地址开始的内存空间中。你需要确保这个缓冲区至少有count
个字节的空间。size_t count
: 这是你希望读取的最大字节数。函数不会读取超过count
个字节的数据。
5. 返回值
read
函数返回实际读取到的字节数,这个数可能小于或等于请求的 count
。
- 成功时:
- 返回实际读取到的字节数 (0 <= 返回值 <=
count
)。 - 如果返回值为 0,通常表示已经到达文件末尾 (End Of File, EOF) 或者没有更多数据可读(例如从管道或网络套接字读取时对端已关闭)。
- 返回实际读取到的字节数 (0 <= 返回值 <=
- 出错时:
- 返回 -1,并设置全局变量
errno
来指示具体的错误类型(例如EAGAIN
、EBADF
、EFAULT
等)。
- 返回 -1,并设置全局变量
6. 相似函数,或关联函数
pread
: 类似于read
,但它允许你在一次调用中同时指定要读取的文件描述符、缓冲区、读取字节数以及文件内的偏移量。它不会改变文件的当前偏移量。readv
: 允许你将数据读入到多个不连续的缓冲区(一个iovec
数组)中,这在处理结构化数据时很有用。write
: 与read
相反,用于将数据写入到文件描述符指向的文件或资源中。open
: 通常在调用read
之前使用,用来获取文件描述符。
7. 示例代码
示例 1:从普通文件读取内容
#include <unistd.h> // read, write, close
#include <fcntl.h> // open, O_RDONLY
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <errno.h> // errno#define BUFFER_SIZE 1024int main() {int fd; // 文件描述符char buffer[BUFFER_SIZE]; // 用于存储读取数据的缓冲区ssize_t bytes_read; // 实际读取的字节数// 1. 打开一个文件以供读取 (假设当前目录下有名为 "example.txt" 的文件)fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("Error opening file"); // 打印错误信息exit(EXIT_FAILURE);}printf("File opened successfully with fd: %d\n", fd);// 2. 循环读取文件内容,直到文件末尾while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) {// 3. 将读取到的内容写入标准输出 (屏幕)// 注意:这里没有处理 write 可能只写入部分数据的情况,// 在实际应用中可能需要循环确保所有数据都被写入。if (write(STDOUT_FILENO, buffer, bytes_read) != bytes_read) {perror("Error writing to stdout");close(fd); // 出错也要记得关闭文件exit(EXIT_FAILURE);}// 可选:打印本次读取了多少字节// printf("Read %zd bytes\n", bytes_read);}// 4. 检查 read 是否因错误而返回if (bytes_read == -1) {perror("Error reading file");close(fd);exit(EXIT_FAILURE);}// 5. 关闭文件描述符if (close(fd) == -1) {perror("Error closing file");exit(EXIT_FAILURE);}printf("\nFinished reading file.\n");return 0;
}
代码解释:
- 定义了缓冲区大小
BUFFER_SIZE
。 - 使用
open()
以只读模式 (O_RDONLY
) 打开名为example.txt
的文件。如果失败,perror()
会打印具体错误原因,程序退出。 - 进入
while
循环,调用read(fd, buffer, BUFFER_SIZE)
。它会尝试读取最多 1024 个字节。 - 如果
read
返回大于 0 的值,说明读到了数据。然后使用write()
将这些数据写到标准输出 (STDOUT_FILENO
,其值为 1)。 - 循环继续,直到
read
返回 0(EOF)。 - 检查
read
是否返回 -1(错误)。 - 最后,使用
close()
关闭文件描述符,释放资源。
示例 2:从标准输入读取一行
这个例子演示如何从键盘(标准输入)读取用户输入的一行文本。
#include <unistd.h> // read
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit#define MAX_INPUT_SIZE 256int main() {char input_buffer[MAX_INPUT_SIZE];ssize_t bytes_read;int i;printf("Enter a line of text (max %d chars): ", MAX_INPUT_SIZE - 1);// 从标准输入 (STDIN_FILENO=0) 读取数据bytes_read = read(STDIN_FILENO, input_buffer, MAX_INPUT_SIZE - 1);if (bytes_read == -1) {perror("Error reading from stdin");exit(EXIT_FAILURE);} else if (bytes_read == 0) {printf("No data read (EOF on stdin?)\n");} else {// 寻找换行符 \n,将其替换为字符串结束符 \0// 这样可以方便地将输入作为 C 字符串处理for (i = 0; i < bytes_read; ++i) {if (input_buffer[i] == '\n') {input_buffer[i] = '\0';break;}}// 如果没有找到 \n (例如输入达到缓冲区最大限制),手动添加 \0if (i == bytes_read) {input_buffer[bytes_read] = '\0';}printf("You entered: %s\n", input_buffer);// 注意:实际读取的字符数可能比 strlen(input_buffer) 多1(因为 \n 被替换了)printf("Number of bytes read (including \\n if present): %zd\n", bytes_read);}return 0;
}
代码解释:
- 提示用户输入。
- 调用
read(STDIN_FILENO, ...)
从键盘读取最多MAX_INPUT_SIZE - 1
个字符(留一个位置给可能添加的\0
)。 - 检查返回值:-1 表示错误,0 表示 EOF,正数表示读取的字节数。
- 为了方便打印,代码查找读入的缓冲区中的换行符
\n
并将其替换为字符串结束符\0
。 - 使用
printf
打印处理后的字符串和实际读取的字节数。
理解 read
函数的关键在于掌握文件描述符的概念以及如何处理其返回值(特别是区分读到数据、到达文件末尾和发生错误的情况)。