398. Java 文件操作基础 - 高级文件操作:随机访问文件

12 阅读2分钟

398. Java 文件操作基础 - 高级文件操作:随机访问文件

在 Java 中,随机访问文件(Random Access Files)允许你 非顺序地 读写文件内容,也就是说,你可以跳到文件中的任意位置读取或写入数据,而不必从头到尾顺序操作。

这种功能通常用于:

  • 大型文件处理(只读取部分内容)
  • 日志文件更新(在文件中间写入新数据)
  • 数据库文件或二进制文件操作

1️⃣ 核心接口:SeekableByteChannel

SeekableByteChannelNIO 提供的 可寻址通道,它扩展了 Channel I/O,并增加了当前位置概念

常用方法:

方法功能
position()获取通道当前的位置(偏移量)
position(long)设置通道当前的位置(跳到文件某个偏移量)
read(ByteBuffer)从当前通道位置读取数据到缓冲区
write(ByteBuffer)将缓冲区的数据写入当前通道位置
truncate(long)截断文件到指定长度

💡 小贴士:通过 Path.newByteChannel()Files.newByteChannel() 可以获得 SeekableByteChannel 实例。在默认文件系统下,也可以将其 强转为 FileChannel,这样可以使用更多高级功能,如文件映射、文件锁、绝对位置读写等。


2️⃣ 示例讲解

假设我们有一个文件 file.txt,我们希望:

  1. 读取文件开头的 12 个字节
  2. 在开头写入 "I was here!"
  3. 将文件开头的 12 个字节复制到文件末尾
  4. 再次写入 "I was here!"
import java.nio.file.*;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import java.io.IOException;

import static java.nio.file.StandardOpenOption.*;

public class RandomAccessDemo {
    public static void main(String[] args) {
        Path file = Paths.get("file.txt");

        String s = "I was here!\n";
        byte[] data = s.getBytes();
        ByteBuffer out = ByteBuffer.wrap(data);       // 包装要写入的数据
        ByteBuffer copy = ByteBuffer.allocate(12);    // 用于存储文件开头的 12 字节

        try (FileChannel fc = FileChannel.open(file, READ, WRITE)) {

            // 1️⃣ 读取文件前 12 个字节
            int nread;
            do {
                nread = fc.read(copy);
            } while (nread != -1 && copy.hasRemaining());

            // 2️⃣ 写入 "I was here!" 到文件开头
            fc.position(0);  // 跳到文件开头
            while (out.hasRemaining()) {
                fc.write(out);
            }
            out.rewind();  // 复位缓冲区,用于后续写入

            // 3️⃣ 移动到文件末尾,并复制前 12 个字节到末尾
            long length = fc.size();
            fc.position(length); // 文件末尾
            copy.flip();         // 切换为读模式
            while (copy.hasRemaining()) {
                fc.write(copy);
            }

            // 4️⃣ 如果需要再次写入 "I was here!" 到文件末尾,需要指定out.rewind();
            while (out.hasRemaining()) {
                fc.write(out);
            }

            System.out.println("文件操作完成!");
        } catch (IOException e) {
            System.err.println("I/O 异常: " + e.getMessage());
        }
    }
}

3️⃣ 核心讲解点

  1. 随机访问的关键是 position()
    • 可以用 fc.position(偏移量) 定位到文件任意位置
    • 后续的 read()write() 都从这个位置开始
  2. 缓冲区管理(ByteBuffer
    • 写入前用 ByteBuffer.wrap()
    • 多次写入需要 out.rewind()flip() 来复位缓冲区
  3. FileChannel vs SeekableByteChannel
    • FileChannel 功能更强大,可实现 文件映射、锁定、绝对位置读写
    • SeekableByteChannel 简单操作即可满足随机访问需求
  4. 异常处理
    • 所有 I/O 操作都可能抛出 IOException
    • 使用 try-with-resources 自动关闭通道,保证资源释放 ✅