当前位置: 首页 > news >正文

Linux文件编程——read函数与lseek函数

一、read函数

在 Linux 文件编程中,read 函数是一个系统调用,用于从文件描述符(File Descriptor)指向的文件或设备中读取数据到缓冲区。它是 Unix/Linux 系统编程中实现底层 I/O 操作的核心函数之一。以下是 read 函数的详细使用方法和注意事项。


1. 函数原型

#include <unistd.h>ssize_t read(int fd, void *buf, size_t count);
  • 参数
    • fd:文件描述符(通过 open 或 creat 等函数获得)。
    • buf:指向存储读取数据的缓冲区的指针。
    • count:要读取的最大字节数。
  • 返回值
    • 成功时返回实际读取的字节数(可能小于 count,例如到达文件末尾时返回 0)。
    • 失败时返回 -1,并设置 errno 表示错误原因。

2. 使用方法

基本示例
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>int main() {int fd;char buf[256];ssize_t bytes_read;// 打开文件(只读模式)fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open failed");return 1;}// 读取数据bytes_read = read(fd, buf, sizeof(buf) - 1); // 保留一个字节给 '\0'if (bytes_read == -1) {perror("read failed");close(fd);return 1;}// 添加字符串终止符并打印buf[bytes_read] = '\0'; // 确保是字符串printf("Read %zd bytes: %s\n", bytes_read, buf);// 关闭文件close(fd);return 0;
}
关键步骤
  1. 打开文件
    • 使用 open 函数获取文件描述符,指定读取模式(如 O_RDONLY)。
  2. 读取数据
    • 调用 read(fd, buf, count),将数据从文件读取到 buf 中。
    • 检查返回值,确保读取成功。
  3. 处理数据
    • 如果读取的是文本数据,通常需要手动添加字符串终止符 \0
  4. 关闭文件
    • 使用 close(fd) 释放文件描述符。

3. 注意事项

(1) 返回值处理
  • 正常情况
    • 返回实际读取的字节数(可能小于 count,例如文件末尾或缓冲区不足)。
    • 返回 0 表示已到达文件末尾(EOF)。
  • 错误情况
    • 返回 -1,需检查 errno(如 EBADFEINTREIO 等)。
  • 示例代码(循环读取)
ssize_t total_read = 0;
while (total_read < count) {ssize_t bytes_read = read(fd, buf + total_read, count - total_read);if (bytes_read == -1) {if (errno == EINTR) continue; // 被信号中断,重试perror("read failed");break;} else if (bytes_read == 0) {break; // EOF}total_read += bytes_read;
}
(2) 错误处理
  • 常见错误包括:
    • EBADF:无效的文件描述符。
    • EINTR:被信号中断(需重试)。
    • EIO:底层 I/O 错误。
    • EAGAIN/EWOULDBLOCK:非阻塞模式下无数据可读(需结合 select/poll 使用)。
  • 使用 perror 或 strerror(errno) 打印错误信息。
(3) 缓冲与非阻塞 I/O
  • read 是无缓冲的(直接调用系统调用),但文件可能因缓冲设置(如 O_NONBLOCK)或设备特性而阻塞。
  • 对于非阻塞文件描述符(如套接字),read 可能返回 EAGAIN 或 EWOULDBLOCK,需结合 select/poll 使用。
(4) 性能优化
  • 批量读取(减少系统调用次数)。
  • 使用 mmap 映射文件到内存(适合大文件随机访问)。
  • 对于大文件,考虑 pread 进行分散/聚集读取(Scatter-Gather I/O)。
(5) 安全性
  • 确保缓冲区 buf 的大小足够,避免缓冲区溢出。
  • 验证文件描述符 fd 的合法性(如通过 fstat 检查)。
  • 避免读取未初始化的数据或越界访问。
(6) 信号中断
  • 如果 read 被信号中断(EINTR),通常需要重新尝试读取。
(7) 二进制 vs 文本数据
  • read 是字节级操作,不关心数据格式(文本或二进制)。
  • 读取文本时需手动处理换行符或编码问题。

4. 替代函数

  • pread:在指定偏移量处读取(无需 lseek)。
  • readv:聚集读取(将数据分散到多个缓冲区)。
  • recv:网络编程中用于套接字的读取(支持标志位)。

5. 常见问题

(1) 为什么 read 返回的字节数小于 count
  • 文件末尾(EOF)。
  • 缓冲区不足。
  • 信号中断(EINTR)。
  • 非阻塞 I/O 且无数据可读(EAGAIN/EWOULDBLOCK)。
(2) 如何确保读取完整数据?
  • 循环读取,直到满足条件(如读取到特定分隔符或固定长度)。
  • 示例(读取固定长度数据):
#define FIXED_SIZE 1024
char buf[FIXED_SIZE];
ssize_t total = 0;
while (total < FIXED_SIZE) {ssize_t ret = read(fd, buf + total, FIXED_SIZE - total);if (ret <= 0) break; // 错误或 EOFtotal += ret;
}
(3) 如何处理大文件?
  • 使用 mmap 映射文件到内存,避免频繁 read
  • 分块读取并处理。

6. 总结

read 是 Linux 文件编程的基础函数,使用时需注意:

  1. 正确处理返回值:区分 EOF、错误和部分读取。
  2. 错误处理:检查 errno 并处理信号中断。
  3. 性能优化:批量读取、避免频繁系统调用。
  4. 安全性:防止缓冲区溢出和非法访问。
  5. 非阻塞 I/O:结合 select/poll 使用。

通过合理使用 read,可以高效、可靠地完成文件 I/O 操作。

二、lseek函数的使用方法

1.函数原型

#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
  • 参数
    • fd:文件描述符。
    • offset:偏移量(字节数),可正可负。
    • whence:基准点,取值如下:
      • SEEK_SET:从文件开头偏移。
      • SEEK_CUR:从当前位置偏移。
      • SEEK_END:从文件末尾偏移。
  • 返回值:成功时返回新的文件偏移量,失败时返回-1并设置errno

2.典型用途

随机访问文件:通过定位到文件的任意位置进行读写。例如,读取文件第1024字节处的512字节数据:

int fd = open("data.dat", O_RDONLY);
lseek(fd, 1024, SEEK_SET); // 定位到文件开头后1024字节处
char buf[512];
read(fd, buf, sizeof(buf));
close(fd);

获取文件大小:通过定位到文件末尾,再获取当前偏移量:

int fd = open("file.txt", O_RDONLY);
off_t size = lseek(fd, 0, SEEK_END); // 偏移量为文件大小
printf("File size: %lld bytes\n", (long long)size);
close(fd);

创建空洞文件:通过将偏移量移动到超过当前文件大小的位置,再写入数据,中间的区域会被视为空洞(不占用磁盘空间):

int fd = open("large_file.dat", O_CREAT | O_WRONLY, 0644);
lseek(fd, 1024 * 1024 * 1024 - 1, SEEK_SET); // 定位到1GB偏移处(最后一个字节)
write(fd, "", 1); // 写入一个字节,文件大小变为1GB
close(fd);

三、lseek函数的注意事项

  1. 返回值检查
    • 成功时返回新的文件偏移量,失败时返回-1,不能用<0判断,因为偏移量可能是负的(如SEEK_CURSEEK_END为基准且offset为负时)。
    • 必须检查返回值以确保操作成功。
  2. 文件类型限制
    • lseek仅适用于支持随机访问的文件(如普通文件),对管道、套接字等流式文件无效。例如,在管道或设备文件上调用lseek会失败,返回-1errno=ESPIPE
  3. 偏移量溢出风险
    • 在32位系统中,off_t为4字节,偏移量超过2GB可能导致溢出。64位系统中,off_t为8字节,可处理更大的文件。
  4. 并发与原子性
    • lseek是原子操作,多线程环境下无需额外同步,但读写操作仍需考虑线程安全。
  5. 文件打开模式的影响
    • 如果文件以追加模式(O_APPEND)打开,lseek设置的偏移量对write操作无效,write仍会追加到文件末尾。

四、lseekread函数的关系

  • 协同工作

lseek用于调整文件的读写位置,read用于从当前位置读取数据。例如:

int fd = open("file.txt", O_RDONLY);
lseek(fd, 100, SEEK_SET); // 定位到第100字节
char buf[10];
read(fd, buf, sizeof(buf)); // 从第100字节开始读取10字节
close(fd);
  • 文件偏移量的更新

read函数会更新文件的当前偏移量,而lseek可以显式地修改偏移量。两者共同决定了下一次读写操作的位置。

  • 性能优化

在需要随机访问文件时,lseek可以避免不必要的顺序读取,提高效率。例如,在数据库文件中,通过lseek直接定位到指定记录的起始位置进行读写操作。

  • 错误处理的关联性

如果lseek调用失败,后续的read操作可能会读取到错误的数据或返回错误。因此,必须在使用lseek后检查其返回值。

http://www.xdnf.cn/news/412489.html

相关文章:

  • 火狐浏览器安装自定义插件
  • 人工智能的哲学与社会影响
  • 【时时三省】(C语言基础)字符数组的输入输出
  • 做好的QT软件,换一个笔记本打开后发现字体很小,部分字体还被控件遮挡
  • 提示工程实战指南:Google白皮书关键内容一文讲清
  • 第二十二天打卡
  • #将一个 .c 文件转变为可直接运行的文件过程及原理
  • CTF实战秘籍:跨平台文件合并与数据重构技术
  • linux-进程信号的产生
  • OJ判题系统第4期之判题机模块架构——设计思路、实现步骤、代码实现(工厂模式、代理模式的实践)
  • 嵌入式MCU和Linux开发哪个好?
  • FreeRTOS的学习记录(基础知识)
  • FPGA----petalinux开机启动自定义脚本/程序的保姆级教程(二)
  • 【超详细教程】安卓模拟器如何添加本地文件?音乐/照片/视频一键导入!
  • 利用基于LLM的概念提取和FakeCTI数据集提升网络威胁情报对抗虚假信息活动的能力
  • 区块链+农业:从田间到餐桌的信任革命
  • Ref是什么
  • 洛谷 P1082:[NOIP 2012 提高组] 同余方程 ← 求逆元
  • 代码随想录训练营第二十二天| 101.对称二叉树 100.相同的树
  • 综合实验二之grub2密文加密
  • (leetcode) 力扣100 10.和为K的子数组(前缀和+哈希)
  • 【Bootstrap V4系列】学习入门教程之 组件-模态框(Modal)
  • css 点击后改变样式
  • Megatron系列——张量并行
  • 我们来学mysql -- 安装8.4版本
  • 在CentOS 7上仅安装部署MySQL 8.0客户端
  • 将arduino开发的Marlin部署到stm32(3D打印机驱动)
  • 【GESP】C++三级练习 luogu-B2156 最长单词 2
  • NeurIPS 2025 截稿攻略
  • 无线传感器网络期末复习自整理资料(天大)