414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件

文章目录

  • [414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件](#414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件)
  • [📚 写入所有 Sonnet](#📚 写入所有 Sonnet)
    • [🎯 核心思路](#🎯 核心思路)
    • [✅ 示例代码](#✅ 示例代码)
    • [🔍 代码解析](#🔍 代码解析)
    • [📌 使用示例](#📌 使用示例)
    • [🚀 总结](#🚀 总结)

414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件

📚 写入所有 Sonnet

前面我们已经学会了如何压缩 单个 Sonnet 。但是莎士比亚总共有 154 首 Sonnet,我们需要把它们都写到一个二进制文件里,方便后续统一存储和读取。

问题是:如果把所有 Sonnet 压缩后直接拼接到一个文件里,后续怎么知道每一首的 位置(offset)长度(length) 呢?

👉 答案是:我们需要额外存储一个"目录表",告诉程序每首诗的起始位置和长度。


🎯 核心思路

最终生成的文件结构如下:

java 复制代码
-----------------------------------
|  Number of sonnets (int)        |
|  Offset[0] (int)                |
|  Length[0] (int)                |
|  Offset[1] (int)                |
|  Length[1] (int)                |
|  ...                            |
|  Offset[n-1] (int)              |
|  Length[n-1] (int)              |
-----------------------------------
|  Compressed sonnets bytes array |
-----------------------------------
  • Number of sonnets → 一共有多少首
  • Offset, Length → 每首 Sonnet 在压缩字节数组中的位置和大小
  • Compressed data → 154 首 Sonnet 的压缩内容

💡 注意:这里的 offset 是相对于 压缩字节数组的起始位置 (不是整个文件开头)。如果想让它相对于整个文件,需要加上 header 的大小:4 + 2*4*numberOfSonnets


✅ 示例代码

java 复制代码
int numberOfSonnets = sonnets.size();
Path sonnetsFile = Path.of("files/sonnets.bin");

try (var sonnetFile = Files.newOutputStream(sonnetsFile);
     var dos = new DataOutputStream(sonnetFile)) {

    List<Integer> offsets = new ArrayList<>();
    List<Integer> lengths = new ArrayList<>();
    byte[] encodedSonnetsBytesArray = null;

    // 第一步:压缩所有 Sonnet 并拼接到内存流
    try (ByteArrayOutputStream encodedSonnets = new ByteArrayOutputStream()) {
        for (Sonnet sonnet : sonnets) {
            byte[] sonnetCompressedBytes = sonnet.getCompressedBytes();

            offsets.add(encodedSonnets.size()); // 当前拼接流的大小就是下一个 Sonnet 的 offset
            lengths.add(sonnetCompressedBytes.length);
            encodedSonnets.write(sonnetCompressedBytes);
        }

        // 第二步:写入文件头
        dos.writeInt(numberOfSonnets);
        for (int index = 0; index < numberOfSonnets; index++) {
            dos.writeInt(offsets.get(index));
            dos.writeInt(lengths.get(index));
        }

        encodedSonnetsBytesArray = encodedSonnets.toByteArray();
    }

    // 第三步:写入所有压缩内容
    sonnetFile.write(encodedSonnetsBytesArray);

} catch (IOException e) {
    e.printStackTrace();
}

🔍 代码解析

  1. offsets.add(encodedSonnets.size())
    • encodedSonnets.size() 返回当前拼接流的长度,也就是下一首 Sonnet 在字节数组中的起始位置。
  2. lengths.add(sonnetCompressedBytes.length)
    • 存储当前 Sonnet 的压缩字节数。
  3. dos.writeInt(numberOfSonnets)
    • 写入总数(方便后续读取时知道有多少首)。
  4. dos.writeInt(offsets.get(index)); dos.writeInt(lengths.get(index));
    • 按顺序写入 offset 和 length,形成一个"索引表"。
  5. sonnetFile.write(encodedSonnetsBytesArray)
    • 最后把所有拼接好的字节流一次性写入文件。

📌 使用示例

运行代码后,会在 files/sonnets.bin 生成一个二进制文件。它的结构大概是这样的:

java 复制代码
[Header]
154 (总数)
0   120 (第1首起始=0, 长度=120字节)
120 130 (第2首起始=120, 长度=130字节)
...
[Body]
[GZIP压缩后的Sonnet字节内容...]

🚀 总结

  1. 单个 Sonnet 压缩 → getCompressedBytes()
  2. 所有 Sonnet 拼接 → ByteArrayOutputStream
  3. 索引表写入 → DataOutputStream
  4. 最终文件结构 = Header + Body

这样,我们就能高效存储所有 154 首 Sonnet,既节省空间,又能快速定位到任意一首诗。

相关推荐
禹凕5 小时前
滑动窗口算法实战指南
python·算法
evans在进步5 小时前
Tomcat NIO 工作原理详解:从 Acceptor、Poller 到容器处理链
java·tomcat·nio
黄一一9112435 小时前
跨平台视频图片压缩互转!离线视频压缩工具!Github开源轻松无损压缩视频!Win/Mac/Linux三端通用开源工具
windows·macos·音视频·开源软件
论文复现现场5 小时前
RTX 4090 24GB 能跑 Qwen3.8-27B 吗?单卡显存计算与云端部署指南
人工智能·python·云计算·llama·gpu算力
Patrick在香港6 小时前
模型路由器实测:12 个任务省 77%,旗舰过载时的降级要打出显式标记
python·llm·智能路由器·api·架构设计·成本优化
Looooking6 小时前
Python 之 jwt 令牌生成和校验
python·jwt
渡我白衣6 小时前
并查集:基础认识与模拟实现
android·java·javascript·数据结构·c++·算法·并查集
电商API_180079052476 小时前
电商商品价格监控工具淘宝京东商品价格抓取API项目实操分享
java·大数据·开发语言·前端·数据挖掘
zzzll11116 小时前
深度剖析:HashMap 并发死循环与 ConcurrentHashMap 两代演进全解
java·开发语言
秋名RG6 小时前
2026/5/27 生产事故复盘与整改方案
java