文件I/O的读写定位

1. read() - 从文件读取数据

read()函数用于从已打开的文件描述符中读取数据。

函数原型

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

参数说明

  • fd :文件描述符,由open()函数返回
  • buf:指向接收数据缓冲区的指针
  • count:期望读取的字节数,不应超过缓冲区大小

返回值

  • 成功:返回实际读取的字节数(0 ≤ 返回值 ≤ count)
  • 文件末尾:返回0,表示已到达文件结尾
  • 出错:返回-1,并设置errno

使用示例

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

int main() {
    int fd = open("example.txt", O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    
    char buffer[1024];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';  // 添加字符串结束符
        printf("Read %zd bytes: %s\n", bytes_read, buffer);
    } else if (bytes_read == 0) {
        printf("End of file reached\n");
    } else {
        perror("read failed");
    }
    
    close(fd);
    return 0;
}

注意事项

  1. read()是阻塞调用,当没有数据可读时会等待
  2. 实际读取的字节数可能小于请求的字节数
  3. 对于网络套接字,read()可能被信号中断

2. write() - 向文件写入数据

write()函数用于向已打开的文件描述符写入数据。

函数原型

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

参数说明

  • fd:文件描述符
  • buf:指向待写入数据缓冲区的指针
  • count:期望写入的字节数

返回值

  • 成功:返回实际写入的字节数(0 ≤ 返回值 ≤ count)
  • 出错:返回-1,并设置errno

使用示例

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

int main() {
    int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    
    const char *message = "Hello, Linux I/O!\n";
    ssize_t bytes_written = write(fd, message, strlen(message));
    
    if (bytes_written == -1) {
        perror("write failed");
    } else {
        printf("Successfully wrote %zd bytes\n", bytes_written);
    }
    
    close(fd);
    return 0;
}

注意事项

  1. 对于普通文件,write()通常写入所有请求的字节
  2. 对于管道或套接字,可能需要多次调用才能写完所有数据
  3. 使用O_APPEND标志打开文件时,写入位置会自动定位到文件末尾

3. lseek() - 定位文件读写位置

lseek()函数用于移动文件读写位置指针,类似于标准C库的fseek()

函数原型

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

参数说明

  • fd:文件描述符
  • offset:偏移量,可为正数、负数或零
  • whence :基准位置,取值为:
    • SEEK_SET:从文件开头计算偏移
    • SEEK_CUR:从当前位置计算偏移
    • SEEK_END:从文件末尾计算偏移

返回值

  • 成功:返回新的文件位置(从文件开头计算的字节偏移)
  • 出错:返回-1,并设置errno

使用示例

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

int main() {
    int fd = open("data.bin", O_RDWR);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    
    // 移动到文件开头后100字节处
    off_t pos = lseek(fd, 100, SEEK_SET);
    if (pos == -1) {
        perror("lseek failed");
    } else {
        printf("Current position: %ld\n", (long)pos);
    }
    
    // 从当前位置向前移动50字节
    pos = lseek(fd, 50, SEEK_CUR);
    printf("New position: %ld\n", (long)pos);
    
    // 获取文件大小
    off_t file_size = lseek(fd, 0, SEEK_END);
    printf("File size: %ld bytes\n", (long)file_size);
    
    close(fd);
    return 0;
}

特殊用法

  1. 获取当前文件位置lseek(fd, 0, SEEK_CUR)
  2. 获取文件大小lseek(fd, 0, SEEK_END)
  3. 创建文件空洞:移动到超过文件末尾的位置后写入数据

4. 综合应用示例

下面是一个完整的示例,演示如何组合使用这三个系统调用:

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

int main() {
    // 创建并写入文件
    int fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open failed");
        return 1;
    }
    
    // 写入数据
    const char *data = "Linux File I/O Example\n";
    write(fd, data, strlen(data));
    
    // 移动到文件开头
    lseek(fd, 0, SEEK_SET);
    
    // 读取并显示数据
    char buffer[256];
    ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0';
        printf("File content: %s", buffer);
    }
    
    // 在文件末尾追加数据
    lseek(fd, 0, SEEK_END);
    const char *append_data = "Appended content\n";
    write(fd, append_data, strlen(append_data));
    
    close(fd);
    return 0;
}