Подтвердить что ты не робот

Как написать средство просмотра текстовых файлов Java для больших файлов журналов

Я работаю над программным продуктом с интегрированным средством просмотра файлов журнала. Проблема в том, что он медленный и нестабильный для действительно больших файлов, потому что он читает весь файл в памяти при просмотре файла журнала. Я хочу написать новый просмотрщик файлов журналов, который решает эту проблему.

Каковы наилучшие методы написания зрителей для больших текстовых файлов? Как это делают редакторы, такие как блокнот ++ и VIM? Я думал об использовании буферизованного двунаправленного текстового потока с Java TableModel. Я думаю о правильных строках и являются ли такие потоковые реализации доступными для Java?

Изменить: будет ли целесообразно запускать файл один раз, чтобы индексировать позиции начала каждой строки текста, чтобы знать, где искать? Мне, вероятно, понадобится количество строк, так что, вероятно, придется просканировать файл хотя бы один раз?

Edit2: Я добавил свою реализацию в ответ ниже. Пожалуйста, прокомментируйте это или отредактируйте его, чтобы помочь мне/нам прийти к более эффективной практике или иным образом предоставить ваши собственные.

4b9b3361

Ответ 1

Я не уверен, что NotePad ++ на самом деле реализует произвольный доступ, но я думаю, что это путь, особенно с помощью средства просмотра журнала, что подразумевает, что он будет доступен только для чтения.

Поскольку ваш просмотрщик журналов будет доступен только для чтения, вы можете использовать только случайный доступ, отображаемый в виде карты "поток". В Java это FileChannel.

Затем просто прыгайте в файле по мере необходимости и визуализируйте на экран только прокручивающееся окно данных.

Одним из преимуществ FileChannel является то, что одновременные потоки могут открыть файл, а чтение не влияет на текущий указатель файла. Итак, если вы добавляете файл журнала в другой поток, это не будет затронуто.

Еще одно преимущество заключается в том, что вы можете вызвать метод размера FileChannel для получения размера файла в любой момент.

Проблема с картографической памятью непосредственно в файле произвольного доступа, которую разрешают некоторые текстовые редакторы (например, HxD и UltraEdit), заключается в том, что любые изменения напрямую влияют на файл. Следовательно, изменения являются немедленными (за исключением кэширования записи), чего обычно не хотят пользователи. Вместо этого пользователи обычно не хотят, чтобы их изменения были сделаны до тех пор, пока они не нажмут кнопку "Сохранить". Однако, поскольку это всего лишь зритель, у вас нет таких же проблем.

Ответ 2

Типичный подход заключается в использовании считываемого файлового считывателя, чтобы пройти один проход через журнал, регистрируя индекс смещений строк, а затем представляя только окно на часть файла в соответствии с запросом.

Это уменьшает как нужные вам данные при быстром отзыве, так и не загружает виджет, где 99% его содержимого в настоящее время не видны.

Ответ 3

Я отправляю свою тестовую реализацию (после того, как вы после консультации с Marcus Adams и msw) здесь для вашего удобства, а также для дальнейших комментариев и критики. Это довольно быстро.

Я не беспокоился о безопасности кодирования Unicode. Думаю, это будет мой следующий вопрос. Любые намеки на это очень приветствуются.

class LogFileTableModel implements TableModel {

    private final File f;
    private final int lineCount;
    private final String errMsg;
    private final Long[] index;
    private final ByteBuffer linebuf = ByteBuffer.allocate(1024);
    private FileChannel chan;

    public LogFileTableModel(String filename) {
        f = new File(filename);
        String m;
        int l = 1;
        Long[] idx = new Long[] {};
        try {
            FileInputStream in = new FileInputStream(f);
            chan = in.getChannel();
            m = null;
            idx = buildLineIndex();
            l = idx.length;
        } catch (IOException e) {
            m = e.getMessage();
        }
        errMsg = m;
        lineCount = l;
        index = idx;
    }

    private Long[] buildLineIndex() throws IOException {
        List<Long> idx = new LinkedList<Long>();
        idx.add(0L);

        ByteBuffer buf = ByteBuffer.allocate(8 * 1024);
        long offset = 0;
        while (chan.read(buf) != -1) {
            int len = buf.position();
            buf.rewind();            
            int pos = 0;
            byte[] bufA = buf.array();
            while (pos < len) {
                byte c = bufA[pos++];
                if (c == '\n')
                    idx.add(offset + pos);
            }
            offset = chan.position();
        }
        System.out.println("Done Building index");
        return idx.toArray(new Long[] {});
    }

    @Override
    public int getColumnCount() {
        return 2;
    }

    @Override
    public int getRowCount() {
        return lineCount;
    }

    @Override
    public String getColumnName(int columnIndex) {
        switch (columnIndex) {
        case 0:
            return "#";
        case 1:
            return "Name";
        }
        return "";
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        switch (columnIndex) {
            case 0:                
                return String.format("%3d", rowIndex);
            case 1:
                if (errMsg != null)
                    return errMsg;
                try { 
                    Long pos = index[rowIndex];
                    chan.position(pos);
                    chan.read(linebuf);
                    linebuf.rewind();
                    if (rowIndex == lineCount - 1)
                        return new String(linebuf.array());
                    else    
                        return new String(linebuf.array(), 0, (int)(long)(index[rowIndex+1]-pos));
                } catch (Exception e) {
                    return "Error: "+ e.getMessage();
                }
        }            
        return "a";
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return String.class;
    }

    // ... other methods to make interface complete


}