Does RandomAccessFile в java читает весь файл в памяти?

Мне нужно прочитать последние n строк из большого файла (скажем, 2 ГБ). Файл кодируется в кодировке UTF-8.

Хотелось бы узнать наиболее эффективный способ сделать это. Читайте о RandomAccessFile в java, но метод seek() читает весь файл в памяти. Он использует встроенную реализацию, поэтому я не смог передать исходный код.

Ответ 1

  1. RandomAccessFile.seek просто устанавливает текущую позицию указателя файла, никакие байты не считываются в память.

  2. Так как ваш файл кодируется в кодировке UTF-8, это текстовый файл. Для чтения текстовых файлов мы обычно используем BufferedReader, Java 7 даже добавил метод удобства File.newBufferedReader для создания экземпляра BufferedReader для чтения текста из файла. Хотя это может быть неэффективно для чтения последних n строк, но их легко реализовать.

  3. Чтобы быть эффективными, нам нужен RandomAccessFile и читать файл назад, начиная с конца. Вот базовый пример

public static void main(String[] args) throws Exception {
    int n = 3;
    List<String> lines = new ArrayList<>();
    try (RandomAccessFile f = new RandomAccessFile("test", "r")) {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        for (long length = f.length(), p = length - 1; p > 0 && lines.size() < n; p--) {
            f.seek(p);
            int b = f.read();
            if (b == 10) {
                if (p < length - 1) {
                    lines.add(0, getLine(bout));
                    bout.reset();
                }
            } else if (b != 13) {
                bout.write(b);
            }
        }
    }
    System.out.println(lines);
}

static String getLine(ByteArrayOutputStream bout) {
    byte[] a = bout.toByteArray();
    // reverse bytes
    for (int i = 0, j = a.length - 1; j > i; i++, j--) {
        byte tmp = a[j];
        a[j] = a[i];
        a[i] = tmp;
    }
    return new String(a);
}

Он читает байт файла после байта, начиная с хвоста до ByteArrayOutputStream, когда достигается LF, он меняет байты и создает строку.

Необходимо улучшить две вещи:

  1. буферизация

  2. Распознавание EOL

Ответ 2

Если вам нужен Random Access, вам понадобится RandomAccessFile. Вы можете преобразовать полученные вами байты в UTF-8, если знаете, что делаете.

Если вы используете BuffredReader, вы можете использовать skip (n) по количеству символов, что означает, что он должен прочитать весь файл.


Способ сделать это в сочетании; заключается в использовании FileInputStream с skip(), найти, где вы хотите читать, путем чтения N новых строк, а затем обернуть поток в BufferedReader, чтобы читать строки с кодировкой UTF-8.