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
相关推荐
夜听莺儿鸣1 天前
502-003_Linux 中断与异常(一):从Linux角度理解中断与异常
linux·中断与异常·linux中断
杨云龙UP1 天前
TDengine 3.4.2.8 Community 三节点三副本生产集群部署实战(DNode/MNode/taosAdapter/Explorer)
大数据·linux·运维·数据库·tdengine·时序库
向成科技1 天前
XC3576H工控主板|深度适配Ubuntu 26.04 LTS,释放边缘AI与工业开发新潜能
linux·人工智能·ubuntu·机器人·硬件·主板·边缘ai
Fcy6481 天前
Linux下 进程间关系与守护进程
linux·运维·服务器·守护进程
码农小韩1 天前
Linux驱动理论(二)——Linux字符设备驱动
linux·嵌入式软件开发·linux操作系统·linux应用开发·linux驱动理论
well06121 天前
Linux粘滞位与Makefile机制深度解析
linux·运维·服务器
再写一行代码就下班1 天前
linux sh脚本在windows修改导致无法使用解决方式
java·linux·centos
额额额对了1 天前
SPI通信
linux·c语言·汇编·单片机·嵌入式硬件·arm
子木HAPPY阳VIP1 天前
Ubuntu 关闭防火墙操作步骤
linux·运维·ubuntu
王振超wzc1 天前
嵌入式开发环境搭建--WM软件安装,Ubuntu操作系统安装
linux·运维·ubuntu