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

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

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


person Vinod Jayachandran    schedule 25.03.2013    source источник
comment
И нет, seek() не читает ничего в память, не говоря уже о файле целиком. У вас есть полный контроль.   -  person NPE    schedule 25.03.2013
comment
Я прочитал этот вопрос, но хотел бы понять, если файл закодирован в UTF-8, то использование RandomAccessFile не рекомендуется?   -  person Vinod Jayachandran    schedule 25.03.2013
comment
Не согласен с дубликатом. Это больше касается RandomAccessFile, а другое больше касается приложения и даже не упоминает RAF.   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 03.03.2015


Ответы (2)


  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);
}

It reads the file byte after byte starting from tail to ByteArrayOutputStream, when LF is reached it reverses the bytes and creates a line.

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

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

  2. Признание EOL

person Evgeniy Dorofeev    schedule 25.03.2013
comment
Можете ли вы указать, как использовать BufferedReader, не читая весь файл? - person Peter Lawrey; 25.03.2013
comment
Поскольку он читает строку за строкой, он не читает весь файл в память. - person Evgeniy Dorofeev; 25.03.2013
comment
Я бы сказал, что, поскольку он читает строку за строкой с самого начала, он считывает весь файл в память, даже если он не загружает весь файл сразу. - person Peter Lawrey; 25.03.2013
comment
Не могли бы вы объяснить, что означают эти числа (10 и 13), которые вы использовали в своем операторе if? - person kalibrain; 01.07.2013
comment
Это новая строка — 1 или 2 символа \n (10) и \r (13), см. en.wikipedia.org /wiki/Новая строка - person Evgeniy Dorofeev; 01.07.2013

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

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


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

person Peter Lawrey    schedule 25.03.2013
comment
Значит ли это, что в конце дня я прочитаю весь файл в памяти? - person Vinod Jayachandran; 25.03.2013
comment
Нет, если ты сделаешь, как я предлагаю. Если вы используете только BufferedReader, он прочитает весь файл, чего я не рекомендую вам делать. - person Peter Lawrey; 25.03.2013
comment
Не могли бы вы поделиться фрагментом кода для этого новичка :(. Я хочу добраться до конца файла, вернуться к n строкам, а затем прочитать n строк в свою память - person Vinod Jayachandran; 25.03.2013
comment
Вам нужно прочитать конец файла как byte[], если вы видите необходимое количество новых строк, вы знаете, куда перейти. Если вы этого не сделаете, вам нужно прочитать еще немного, пока вы не сделаете. Примечание: если в файле нет новых строк, вы можете прочитать весь файл. Предлагаю попробовать ;) - person Peter Lawrey; 25.03.2013