Linux 文件操作详解:open/read/write/lseek 系统调用

一、系统调用 vs 标准库函数

Linux 下操作文件有两种方式:

类型 函数 头文件 返回值
标准库函数 fopen / fclose / fread / fwrite <stdio.h> FILE* 文件指针
系统调用 open / close / read / write <fcntl.h> <unistd.h> int 文件描述符

区别

  • 标准库函数是 C 标准库提供的,带缓冲区,跨平台

  • 系统调用是操作系统提供的接口,无缓冲区,直接操作内核

二、文件描述符

文件描述符是 open 函数打开文件后返回的整数,用来标识一个打开的文件。

文件描述符 对应流 说明
0 stdin 标准输入(键盘)
1 stdout 标准输出(屏幕)
2 stderr 标准错误输出(屏幕)
  • 新打开的文件从 3 开始递增

  • 上限受系统文件表限制

三、open() - 打开/创建文件

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

int open(const char* pathname, int flags);
int open(const char* pathname, int flags, mode_t mode);

参数详解

参数 含义 常见取值
pathname 文件路径 "test.txt", "/home/user/file"
flags 打开方式 见下表
mode 权限(仅创建时有效) 0644, 0666

flags 取值

必选其一

含义
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写

可选组合

含义
O_CREAT 文件不存在则创建
O_APPEND 追加到末尾
O_TRUNC 清空文件

mode 权限

cpp 复制代码
0644  // rw-r--r--  所有者可读写,其他人只读
0666  // rw-rw-rw-  所有人可读写

返回值

  • 成功:返回文件描述符(非负整数,如 3、4、5...)

  • 失败:返回 -1

cpp 复制代码
int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
if (fd == -1) {
    perror("open");
    exit(1);
}

四、close() - 关闭文件

cpp 复制代码
#include <unistd.h>
int close(int fd);

参数

参数 含义
fd 文件描述符(open 的返回值)

返回值

  • 成功:返回 0

  • 失败:返回 -1

cpp 复制代码
close(fd);

五、write() - 写入文件

cpp 复制代码
#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t count);

参数

参数 含义 示例
fd 文件描述符 fd
buf 要写入的数据的指针 "Hello", &ch, arr
count 要写入的字节数 strlen(str), sizeof(buf)

返回值

  • 成功:返回实际写入的字节数

  • 失败:返回 -1

cpp 复制代码
char* str = "Hello World";
ssize_t ret = write(fd, str, strlen(str));
if (ret == -1) {
    perror("write");
}

六、read() - 读取文件

cpp 复制代码
#include <unistd.h>
ssize_t read(int fd, void* buf, size_t count);

参数

参数 含义 示例
fd 文件描述符 fd
buf 存放读取数据的缓冲区 char buf[1024]
count 要读取的最大字节数 sizeof(buf)

返回值

  • 成功:返回实际读取的字节数(0 表示读到文件末尾)

  • 失败:返回 -1

cpp 复制代码
char buf[1024];
ssize_t ret = read(fd, buf, sizeof(buf) - 1);
if (ret == -1) {
    perror("read");
} 
else if (ret == 0) {
    printf("文件末尾\n");
} 
else {
    buf[ret] = '\0';  // 添加字符串结束符
    printf("读取 %ld 字节: %s\n", ret, buf);
}

七、lseek() - 移动文件偏移量

文件读写时存在偏移量 (当前读写位置),lseek 可以主动设置该偏移量。

cpp 复制代码
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

参数

参数 含义 取值
fd 文件描述符 open() 的返回值
offset 偏移量(字节数) 正数(向后)、负数(向前)、0
whence 基准位置 见下表

whence 的三种取值

数值 含义
SEEK_SET 0 文件开头开始偏移
SEEK_CUR 1 当前位置开始偏移
SEEK_END 2 文件末尾开始偏移

常见用法

cpp 复制代码
// 获取当前位置
off_t pos = lseek(fd, 0, SEEK_CUR);

// 移动到文件开头
lseek(fd, 0, SEEK_SET);

// 移动到文件末尾
lseek(fd, 0, SEEK_END);

// 获取文件大小
off_t size = lseek(fd, 0, SEEK_END);

// 从当前位置向后移动 10 字节
lseek(fd, 10, SEEK_CUR);

返回值

  • 成功:返回新的文件偏移量(从文件开头计算的字节数)

  • 失败:返回 -1

八、fgets() - 标准库读取一行

cpp 复制代码
#include <stdio.h>
char* fgets(char* s, int size, FILE* stream);

参数

参数 含义 示例
s 存放字符串的缓冲区 char buf[256]
size 最多读取 size-1 个字符 sizeof(buf)
stream 文件流指针 stdin(标准输入), fp(文件指针)

返回值

  • 成功:返回 s(缓冲区地址)

  • 失败或文件末尾:返回 NULL

特点

  • 自动添加 \0

  • 会读取换行符 \n(与 gets 的区别)

cpp 复制代码
FILE* fp = fopen("test.txt", "r");
char buf[256];
while (fgets(buf, sizeof(buf), fp) != NULL) {
    printf("%s", buf);  // buf 中已包含换行符
}
fclose(fp);

九、完整示例

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

int main() {
    // 1. 打开/创建文件
    int fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    // 2. 写入数据
    char* str = "Hello Linux File Operation\n";
    ssize_t ret = write(fd, str, strlen(str));
    if (ret == -1) {
        perror("write");
        close(fd);
        return 1;
    }
    printf("写入 %ld 字节\n", ret);
    
    // 3. 移动偏移量到文件开头
    lseek(fd, 0, SEEK_SET);
    
    // 4. 读取数据
    char buf[256];
    ret = read(fd, buf, sizeof(buf) - 1);
    if (ret == -1) {
        perror("read");
        close(fd);
        return 1;
    }
    buf[ret] = '\0';
    printf("读取 %ld 字节: %s", ret, buf);
    
    // 5. 关闭文件
    close(fd);
    
    return 0;
}

十、总结

知识点 核心要点
系统调用 vs 标准库 系统调用无缓冲、直接操作内核;标准库带缓冲、跨平台
文件描述符 0(stdin)、1(stdout)、2(stderr),新文件从 3 开始
open() O_RDONLY/O_WRONLY/O_RDWR + O_CREAT/O_APPEND/O_TRUNC
read()/write() 返回实际读/写字节数,0 表示文件末尾,-1 表示错误
lseek() SEEK_SET(开头)、SEEK_CUR(当前)、SEEK_END(末尾)
fgets() 自动加 \0,会读取 \n,读到末尾返回 NULL
相关推荐
可可西里_X_back1 小时前
Linux学习(二)- 驱动开发步骤
linux·驱动开发·学习
Hical_W1 小时前
Hical 踩坑实录五部曲(二):MSVC / GCC / Clang 三平台 C++20 编译差异
linux·windows·经验分享·嵌入式硬件·macos·开源·c++20
活蹦乱跳酸菜鱼2 小时前
linux ATF BL2执行过程
linux
ai_coder_ai3 小时前
在自动化脚本中如何在自己的后端服务中调用open api获取所有设备信息?
autojs·open·自动化脚本·冰狐智能辅助·easyclick·open api
淡淡烟雨淡淡愁4 小时前
安装libreoffice
linux
蜀道山老天师4 小时前
云原生监控入门:监控基础概念 + SLI/SLO/SLA 详解 + Prometheus 从零安装配置
linux·运维·云原生·prometheus
AIDF20264 小时前
linux 服务器网络问题排查
linux·服务器·网络
楼兰公子4 小时前
br_opi5_plus_defconfig 附带uboot
linux·运维·服务器
mzhan0175 小时前
Linux: signal: SIGALRM; alarm: ITIMER_REAL
linux·运维·服务器