415. Java 文件操作基礎 - 精準讀取壓縮詩集:從二進制文件中高效提取指定十四行詩

415. Java 文件操作基礎 - 精準讀取壓縮詩集:從二進制文件中高效提取指定十四行詩

?? 讀取單個 Sonnet

上節(jié)課我們已經把所有 154 首 Sonnet 壓縮存儲在一個二進制文件里。
這節(jié)課我們來看看:如何只讀取其中的一首(比如第 75 首)。


?? 核心思路

讀取邏輯其實比寫入簡單:

  1. 讀取文件頭:獲取總數、offset 和 length。
  2. 定位目標 Sonnet:根據 offset 跳過前面的字節(jié)。
  3. 讀取壓縮字節(jié)數組
  4. 解壓縮 + 解碼成文本。

? 示例代碼(讀取文件頭)

Path path = Paths.get("files/sonnets.bin");

try (InputStream file = Files.newInputStream(path);
     BufferedInputStream bis = new BufferedInputStream(file);
     DataInputStream dis = new DataInputStream(file)) {

    int numberOfSonnets = dis.readInt();
    System.out.println("numberOfSonnets = " + numberOfSonnets);

    List<Integer> offsets = new ArrayList<>();
    List<Integer> lengths = new ArrayList<>();
    for (int i = 0; i < numberOfSonnets; i++) {
        offsets.add(dis.readInt());
        lengths.add(dis.readInt());
    }

    // 此時已經拿到 offsets 和 lengths
} catch (IOException e) {
    e.printStackTrace();
}

?? 運行后,你會得到類似輸出:

numberOfSonnets = 154

并且 offsetslengths 數組里保存了每首 Sonnet 的位置信息。


?? 跳過和讀取字節(jié)的工具方法

?? 注意:

  • skip(n)read(n) 在流上操作時,可能不會一次完成任務(尤其是大文件)。
  • 所以我們要寫工具方法,確保真的跳過/讀取了指定字節(jié)數。

跳過固定字節(jié)數

static long skip(BufferedInputStream bis, int offset) throws IOException {
    long skipped = 0L;
    while (skipped < offset) {
        skipped += bis.skip(offset - skipped);
    }
    return skipped;
}

讀取固定字節(jié)數

static byte[] readBytes(BufferedInputStream bis, int length) throws IOException {
    byte[] bytes = new byte[length];
    int copied = 0;

    while (copied < length) {
        int read = bis.read(bytes, copied, length - copied);
        if (read == -1) throw new EOFException("Unexpected end of file");
        copied += read;
    }
    return bytes;
}

?? 我稍微優(yōu)化了原代碼:不再額外使用中間 buffer,直接往目標數組里讀,邏輯更直觀。


? 示例代碼(讀取第 75 首 Sonnet)

int sonnetIndex = 75; // 要讀取的 Sonnet
int offset = offsets.get(sonnetIndex - 1);
int length = lengths.get(sonnetIndex - 1);

skip(bis, offset);                // 定位到 offset
byte[] bytes = readBytes(bis, length); // 讀取壓縮字節(jié)數組

try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
     GZIPInputStream gzis = new GZIPInputStream(bais);
     InputStreamReader isr = new InputStreamReader(gzis);
     BufferedReader reader = new BufferedReader(isr)) {

    List<String> sonnetLines = reader.lines().toList();
    sonnetLines.forEach(System.out::println);
}

?? 輸出結果

運行后,你會在控制臺看到 第 75 首 Sonnet(解壓縮后的文本):

  So are you to my thoughts as food to life,
  Or as sweet-season’d showers are to the ground;
  And for the peace of you I hold such strife
  As ’twixt a miser and his wealth is found.
  Now proud as an enjoyer, and anon
  Doubting the filching age will steal his treasure;
  Now counting best to be with you alone,
  Then better’d that the world may see my pleasure:
  Sometime all full with feasting on your sight,
  And by and by clean starved for a look;
  Possessing or pursuing no delight,
  Save what is had, or must from you be took.
    Thus do I pine and surfeit day by day,
    Or gluttoning on all, or all away.

?? 總結

  1. 讀取文件頭 → 拿到目錄表(offset & length)
  2. 用 skip() 精確跳過字節(jié)
  3. 用 readBytes() 保證完整讀取
  4. 解壓縮 + 轉換為字符串行
  5. 最后打印目標 Sonnet

這就是從壓縮二進制文件里 精確定位并讀取單個 Sonnet 的完整流程。


要不要我?guī)湍惆?讀取單個 Sonnet 的邏輯,封裝成一個 SonnetFileReader 工具類?這樣學員就可以像調用 API 一樣使用,比如:

SonnetFileReader sfr = new SonnetFileReader("files/sonnets.bin");
List<String> sonnet75 = sfr.readSonnet(75);
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容