文章目录
- 414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件
- 📚 写入所有 Sonnet
- 🎯 核心思路
- ✅ 示例代码
- 🔍 代码解析
- 📌 使用示例
- 🚀 总结
414. Java 文件操作基础 - 批量压缩与索引:将154首十四行诗高效存储为带目录的二进制文件
📚 写入所有 Sonnet
前面我们已经学会了如何压缩单个 Sonnet。但是莎士比亚总共有154 首 Sonnet,我们需要把它们都写到一个二进制文件里,方便后续统一存储和读取。
问题是:如果把所有 Sonnet 压缩后直接拼接到一个文件里,后续怎么知道每一首的位置(offset)和长度(length)呢?
👉 答案是:我们需要额外存储一个“目录表”,告诉程序每首诗的起始位置和长度。
🎯 核心思路
最终生成的文件结构如下:
-----------------------------------|Numberof sonnets(int)||Offset[0](int)||Length[0](int)||Offset[1](int)||Length[1](int)||...||Offset[n-1](int)||Length[n-1](int)|-----------------------------------|Compressedsonnets bytes array|------------------------------------ Number of sonnets→ 一共有多少首
- Offset, Length→ 每首 Sonnet 在压缩字节数组中的位置和大小
- Compressed data→ 154 首 Sonnet 的压缩内容
💡 注意:这里的 offset 是相对于压缩字节数组的起始位置(不是整个文件开头)。如果想让它相对于整个文件,需要加上 header 的大小:
4 + 2*4*numberOfSonnets。
✅ 示例代码
intnumberOfSonnets=sonnets.size();PathsonnetsFile=Path.of("files/sonnets.bin");try(varsonnetFile=Files.newOutputStream(sonnetsFile);vardos=newDataOutputStream(sonnetFile)){List<Integer>offsets=newArrayList<>();List<Integer>lengths=newArrayList<>();byte[]encodedSonnetsBytesArray=null;// 第一步:压缩所有 Sonnet 并拼接到内存流try(ByteArrayOutputStreamencodedSonnets=newByteArrayOutputStream()){for(Sonnetsonnet:sonnets){byte[]sonnetCompressedBytes=sonnet.getCompressedBytes();offsets.add(encodedSonnets.size());// 当前拼接流的大小就是下一个 Sonnet 的 offsetlengths.add(sonnetCompressedBytes.length);encodedSonnets.write(sonnetCompressedBytes);}// 第二步:写入文件头dos.writeInt(numberOfSonnets);for(intindex=0;index<numberOfSonnets;index++){dos.writeInt(offsets.get(index));dos.writeInt(lengths.get(index));}encodedSonnetsBytesArray=encodedSonnets.toByteArray();}// 第三步:写入所有压缩内容sonnetFile.write(encodedSonnetsBytesArray);}catch(IOExceptione){e.printStackTrace();}🔍 代码解析
offsets.add(encodedSonnets.size())encodedSonnets.size()返回当前拼接流的长度,也就是下一首 Sonnet 在字节数组中的起始位置。
lengths.add(sonnetCompressedBytes.length)- 存储当前 Sonnet 的压缩字节数。
dos.writeInt(numberOfSonnets)- 写入总数(方便后续读取时知道有多少首)。
dos.writeInt(offsets.get(index)); dos.writeInt(lengths.get(index));- 按顺序写入 offset 和 length,形成一个“索引表”。
sonnetFile.write(encodedSonnetsBytesArray)- 最后把所有拼接好的字节流一次性写入文件。
📌 使用示例
运行代码后,会在files/sonnets.bin生成一个二进制文件。它的结构大概是这样的:
[Header]154(总数)0120(第1首起始=0,长度=120字节)120130(第2首起始=120,长度=130字节)...[Body][GZIP压缩后的Sonnet字节内容...]🚀 总结
- 单个 Sonnet 压缩 →
getCompressedBytes() - 所有 Sonnet 拼接 →
ByteArrayOutputStream - 索引表写入 →
DataOutputStream - 最终文件结构 =
Header + Body
这样,我们就能高效存储所有 154 首 Sonnet,既节省空间,又能快速定位到任意一首诗。