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
相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩3 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言