深入解析Linux下的`lseek`函数:文件定位与操作的艺术

深入解析Linux下的`lseek`函数:文件定位与操作的艺术

在Linux系统编程中,lseek函数是一个不可或缺的工具,它允许开发者精确控制文件指针的位置,从而实现高效的文件读取、写入和扩展操作。本文将深入探讨lseek函数的各个方面,包括其功能、使用场景、实际应用案例以及与其他类似函数的比较,帮助开发者全面掌握这一重要工具。


1. lseek函数的功能与参数

lseek函数用于改变与文件描述符关联的文件指针的位置。其函数原型如下:

c 复制代码
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

参数说明:

  • fd :文件描述符,必须有效且已打开。
  • offset :偏移量,表示从whence指定的起始点开始移动的距离。
  • whence :指定起始点,取值为:
    • SEEK_SET:文件开头。
    • SEEK_CUR:当前文件指针位置。
    • SEEK_END:文件末尾。

返回值:

  • 成功时,返回新的文件指针位置(以字节为单位)。
  • 失败时,返回-1,并设置errno

错误处理:

  • EBADF:文件描述符无效。
  • EINVALwhence参数无效。
  • ESPIPE:尝试对管道、套接字或类似设备进行写入操作。

2. 写完文件再读

在Linux中,写完文件后,文件指针位于文件末尾。如果需要读取文件内容,必须先调整文件指针的位置。

示例代码:

c 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 写入数据
    char* data = "Hello, World!";
    write(fd, data, strlen(data));

    // 写完后,文件指针在末尾,无法直接读取
    // 需要调整文件指针到开头
    if (lseek(fd, 0, SEEK_SET) == -1) {
        perror("lseek");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 以读模式打开文件(注意:必须重新打开文件,因为写模式下无法读取)
    close(fd);
    fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 读取数据
    char buffer[1024];
    int bytes_read = read(fd, buffer, sizeof(buffer)-1);
    if (bytes_read == -1) {
        perror("read");
        close(fd);
        exit(EXIT_FAILURE);
    }
    buffer[bytes_read] = '\0';
    printf("Read: %s\n", buffer);

    close(fd);
    return 0;
}

注意事项:

  • 写入文件后,文件描述符处于写模式,无法直接读取。需要重新以读模式打开文件。
  • 使用lseek调整文件指针位置时,必须确保文件描述符有效。

3. 使用lseek读取文件大小

lseek可以用来获取文件的大小。通过将文件指针移动到文件末尾,可以得到文件的总字节数。

示例代码:

c 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 移动文件指针到文件末尾
    off_t file_size = lseek(fd, 0, SEEK_END);
    if (file_size == -1) {
        perror("lseek");
        close(fd);
        exit(EXIT_FAILURE);
    }

    printf("File size: %ld bytes\n", file_size);

    close(fd);
    return 0;
}

注意事项:

  • 文件必须以读模式打开,否则可能导致错误。
  • lseek返回的是文件指针的新位置,即文件的总大小。

4. 使用lseek扩展文件大小

可以通过lseek将文件指针移动到所需位置,然后写入数据来扩展文件大小。

示例代码:

c 复制代码
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 写入初始数据
    char* data = "Hello, World!";
    write(fd, data, strlen(data));

    // 移动文件指针到新的位置(扩展文件)
    off_t new_size = 1024; // 扩展到1024字节
    if (lseek(fd, new_size - 1, SEEK_SET) == -1) {
        perror("lseek");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 写入一个字节来扩展文件
    char zero = '\0';
    write(fd, &zero, 1);

    close(fd);
    return 0;
}

注意事项:

  • 移动文件指针到new_size - 1,然后写入一个字节,以确保文件大小扩展到new_size
  • 文件必须以写模式打开,否则可能导致错误。

5. 与标准库fseek函数的比较

fseek是C标准库函数,用于调整文件指针的位置。以下是lseekfseek的比较:

属性 lseek fseek
参数 int fd FILE *stream
返回值 off_t int
文件描述符 使用文件描述符 使用FILE指针
处理方式 系统调用 库函数
错误处理 设置errno 返回非零值表示错误

示例代码:

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    // 使用`lseek`
    if (lseek(fd, 10, SEEK_SET) == -1) {
        perror("lseek");
        close(fd);
        exit(EXIT_FAILURE);
    }

    // 使用`fseek`
    FILE *fp = fdopen(fd, "r+");
    if (fseek(fp, 10, SEEK_SET) != 0) {
        perror("fseek");
        fclose(fp);
        exit(EXIT_FAILURE);
    }

    fclose(fp);
    close(fd);
    return 0;
}

注意事项:

  • lseek是系统调用,性能更高,但需要处理文件描述符。
  • fseek是库函数,使用更方便,但性能稍低。

总结

lseek函数在Linux系统编程中是一个非常有用的工具,能够帮助开发者精确控制文件指针的位置。通过本文的介绍,读者可以更好地理解lseek的用法及其在实际开发中的应用。希望本文对您有所帮助!

相关推荐
guygg882 分钟前
一维移动最小二乘近似的MATLAB程序
开发语言·matlab
一分之二~5 分钟前
回溯算法--递增子序列
开发语言·数据结构·算法·leetcode
一水鉴天6 分钟前
整体设计 定稿 之6 完整设计文档讨论及定稿 之2 模块化设计体系规范(工具作为首批践行者)(豆包助手)
运维·人工智能·重构·架构
艾莉丝努力练剑7 分钟前
【Python库和代码案例:第一课】Python 标准库与第三方库实战指南:从日期处理到 Excel 操作
java·服务器·开发语言·人工智能·python·pycharm·pip
Yu_Lijing7 分钟前
基于C++的《Head First设计模式》笔记——策略模式
c++·笔记·设计模式
十月南城10 分钟前
统一日志与链路ID的价值——为什么要结构化日志、如何通过关联ID提升排障效率
运维·网络
yugi98783810 分钟前
基于C#实现的WiFi信号强度扫描程序
开发语言·c#
huangyuchi.11 分钟前
【Linux网络】UDP协议详解:透过源码看透“面向数据报”与“缓冲区”的本质
linux·网络·udp·报文·linux网络·传输层协议·报头
林九生11 分钟前
【Centos7】CentOS 7 yum源失效解决方案:Could not resolve host mirrorlist.centos.org
linux·运维·centos
乂爻yiyao12 分钟前
Java 的云原生困局与破局
java·开发语言·云原生