簡體   English   中英

結合使用RandomAccessFile和BufferedReader來加速文件讀取

[英]Using RandomAccessFile along with BufferedReader to speed up file read

我必須 :-

  • 逐行讀取大文本文件。
  • 每一行讀取后記下文件指針的位置。
  • 如果運行時間大於30秒,則停止讀取文件。
  • 從新過程中最后記下的文件指針恢復。

我在做什么 :

  1. 使用RandomAccessFile.getFilePointer()記錄文件指針。
  2. 根據答案,將RandomAccessFile包裝到另一個BufferedReader中,以加快文件讀取過程。
  3. 當時間大於30秒時,我停止讀取文件。 使用新的RandomAccessFile重新啟動過程,並使用RandomAccessFile.seek方法將文件指針移動到我離開的地方。

問題:

當我閱讀包裹在RandomAccessFile周圍的BufferedReader時,似乎在單次調用 BufferedReader.readLine()時文件指針就向前移動了很多 但是,如果我直接使用RandomAccessFile.readLine(),則文件指針會向前正確逐步移動。

使用BufferedReader作為包裝器:

    RandomAccessFile randomAccessFile = new RandomAccessFile("mybigfile.txt", "r");
BufferedReader brRafReader = new BufferedReader(new FileReader(randomAccessFile.getFD()));
while((line = brRafReader.readLine()) != null) {
    System.out.println(line+", Position : "+randomAccessFile.getFilePointer());
}

輸出:

Line goes here, Position : 13040
Line goes here, Position : 13040
Line goes here, Position : 13040
Line goes here, Position : 13040

使用直接RandomAccessFile.readLine

    RandomAccessFile randomAccessFile = new RandomAccessFile("mybigfile.txt", "r");
while((line = randomAccessFile.readLine()) != null) {
    System.out.println(line+", Position : "+randomAccessFile.getFilePointer());
}

輸出:(這是預期的。每次讀取readline時文件指針都會正確移動)

Line goes here, Position : 11011
Line goes here, Position : 11089
Line goes here, Position : 12090
Line goes here, Position : 13040

誰能說,我在這里做什么錯? 有什么方法可以使用RandomAccessFile加快閱讀速度嗎?

觀察到該行為的原因是,顧名思義, BufferedReaderbuffered 它讀取數據的一次更大的塊(入緩沖液),並且僅返回的緩沖區的內容的相關部分-即部分到下一個\\n行分隔符。

我認為,從廣義上講,有兩種可能的方法:

  1. 您可以實現自己的緩沖邏輯。
  2. 使用一些丑陋的反射技巧獲得所需的緩沖區偏移量

對於1.,您將不再使用RandomAccessFile#readLine 相反,您可以通過自己進行緩沖

byte buffer[] = new byte[8192];
...
// In a loop:
int read = randomAccessFile.read(buffer);
// Figure out where a line break `\n` appears in the buffer,
// return the resulting lines, and take the position of the `\n`
// into account when storing the "file pointer"

正如模糊的評論所表明的那樣:這可能既麻煩又麻煩。 您基本上可以重新實現BufferedReader類中的readLine方法。 在這一點上,我什至不想提及不同的行分隔符或字符集可能引起的麻煩。

對於2.,您可以簡單地訪問存儲緩沖區偏移量的BufferedReader的字段。 這在下面的示例中實現。 當然,這是一個粗略的解決方案,但是在此提及並顯示為簡單的替代方案,具體取決於解決方案的“可持續性”以及您願意投入多少精力。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class LargeFileRead {
    public static void main(String[] args) throws Exception {

        String fileName = "myBigFile.txt";

        long before = System.nanoTime();
        List<String> result = readBuffered(fileName);
        //List<String> result = readDefault(fileName);
        long after = System.nanoTime();
        double ms = (after - before) / 1e6;
        System.out.println("Reading took " + ms + "ms "
                + "for " + result.size() + " lines");
    }

    private static List<String> readBuffered(String fileName) throws Exception {
        List<String> lines = new ArrayList<String>();
        RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "r");
        BufferedReader brRafReader = new BufferedReader(
                new FileReader(randomAccessFile.getFD()));
        String line = null;
        long currentOffset = 0;
        long previousOffset = -1;
        while ((line = brRafReader.readLine()) != null) {
            long fileOffset = randomAccessFile.getFilePointer();
            if (fileOffset != previousOffset) {
                if (previousOffset != -1) {
                    currentOffset = previousOffset;
                }
                previousOffset = fileOffset;
            }
            int bufferOffset = getOffset(brRafReader);
            long realPosition = currentOffset + bufferOffset;
            System.out.println("Position : " + realPosition 
                    + " with FP " + randomAccessFile.getFilePointer()
                    + " and offset " + bufferOffset);
            lines.add(line);
        }
        return lines;
    }

    private static int getOffset(BufferedReader bufferedReader) throws Exception {
        Field field = BufferedReader.class.getDeclaredField("nextChar");
        int result = 0;
        try {
            field.setAccessible(true);
            result = (Integer) field.get(bufferedReader);
        } finally {
            field.setAccessible(false);
        }
        return result;
    }

    private static List<String> readDefault(String fileName) throws Exception {
        List<String> lines = new ArrayList<String>();
        RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "r");
        String line = null;
        while ((line = randomAccessFile.readLine()) != null) {
            System.out.println("Position : " + randomAccessFile.getFilePointer());
            lines.add(line);
        }
        return lines;
    }
}

(注意:偏移量似乎仍然偏移了1,但這是由於未在該位置考慮行分隔符。可以根據需要進行調整)

注意:這只是一個草圖。 完成讀取后,RandomAccessFile對象應正確關閉,但這取決於在超過時間限制時應如何中斷讀取,如問題所述

BufferedReader從文件中讀取數據塊,默認情況下為8 KB。 在緩沖區中查找要返回下一行的換行符。

我猜,這就是為什么您看到物理文件位置大幅增加的原因。

讀取下一行時,RandomAccessFile將不使用緩沖區。 它將逐字節讀取。 真的很慢

當您僅使用BufferedReader並記住需要繼續的那一行時,性能如何?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM