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
相关推荐
StarsGreen2 小时前
libva在intel平台上的前端流程简介
linux
坤坤子吖5 小时前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json
Felven6 小时前
华为鲲鹏920s芯片serdestool工具使用说明
linux·运维·服务器·华为·鲲鹏920·serdestool
莫晓燚7 小时前
mall 电商项目完整部署---实战
linux·运维·docker·容器
咋的啥名都用不了呢~7 小时前
Linux项目部署5(安装Nginx的插件)
linux
CodeYp20197 小时前
第11讲:应用层协议 HTTP
linux·http
爱和冰阔落7 小时前
【Linux网络】JsonCpp 怎么实现序列化?从 Request/Response 到网络计算器消息设计
linux·运维·网络·php
我是koten13 小时前
【运维杂谈】K8s遇到Terminating的名称空间删除不了怎么办?(解决方案亲测有效)
linux·运维·服务器·云原生·kubernetes·k8s·名称空间
2601_9679873721 小时前
安装 Redis 或 Redis Stack
linux·windows·redis·macos·安装
feng_you_ying_li21 小时前
linux之数据链路层
linux·网络·智能路由器