加载和显示大型文本文件

在Swing应用程序中,我有时需要支持对加载缓慢的大型面向行的文本文件的只读访问:日志,转储,跟踪等。对于少量数据, 合适的 DocumentJTextComponent都可以,如图所示。 我理解浏览大量数据的人为限制,但有问题的东西似乎总是在最大的文件中。 对于10-100兆字节,百万行范围内的大量文本,是否有任何实用的替代方案?

我会将问题分开。

第一个是模型 – 文档构建速度

第二个是Document rendering – 构建视图树来表示Document。

问题是你是否需要关键字着色等字体效果?

我将从文档构建部分开始。 通过EditorKit.read()读取文件的IMHO即使对于大文件也应该快速。 我会使用PainDocument来检查纯模型是否构建得足够快,适合您的应用程序。 如果是的话,只需使用Document作为模型。 如果没有实现自己的Document接口,因为AbstractDocument有很多方法可以进行更新处理(例如writeLock)。

当我们足够快地加载文档时,我们必须解决文档渲染问题。 默认情况下,javax.swing.text中使用的视图非常灵活。 它们被设计为要扩展的基类 – 因此有很多我们不需要的代码。 例如测量。

对于该function,我将使用Monospaced字体,我们不需要换行,因此视图的测量值是快=最长的行字符数* char widht。

高度也是char高度*线的数量。

所以我们的PLainTextViewReplacement非常快。 此外,我们不必渲染整个视图,只需在滚动窗格中显示一个片段。 因此渲染可以快得多。

当然应该有很多工作来提供正确的插入符导航,选择等。

由于大小,您肯定希望在后台加载文件以避免阻塞事件派发线程 ; SwingWorker是一种常见的选择。 不要使用Document ,而是考虑更新TableModel并在JTable的行中显示文本行。 这提供了几个优点:

  • 结果将立即开始出现,并且将减少感知延迟。

  • JTable使用flyweight模式进行渲染 ,可以很好地扩展到数兆字节的百万行范围。

  • 您可以在读取时解析输入以创建任意列结构。

  • 例如,您可以利用JTable的排序和过滤function。

  • 您可以使用TablePopupEditor专注于单行。

附录:下面的示例使用DefaultTableModel以方便使用。 要减少开销 ,请扩展AbstractTableModel并管理ListList ,如下所示。 该示例显示了不确定的进度; 此处显示了显示中间进度的更改。

码:

 import java.awt.BorderLayout; import java.awt.EventQueue; import java.beans.PropertyChangeEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.util.List; import javax.swing.JFrame; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingWorker; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; /** * @see https://stackoverflow.com/a/25526869/230513 */ public class DisplayLog { private static final String NAME = "/var/log/install.log"; private static class LogWorker extends SwingWorker { private final File file; private final DefaultTableModel model; private LogWorker(File file, DefaultTableModel model) { this.file = file; this.model = model; model.setColumnIdentifiers(new Object[]{file.getAbsolutePath()}); } @Override protected TableModel doInBackground() throws Exception { BufferedReader br = new BufferedReader(new FileReader(file)); String s; while ((s = br.readLine()) != null) { publish(s); } return model; } @Override protected void process(List chunks) { for (String s : chunks) { model.addRow(new Object[]{s}); } } } private void display() { JFrame f = new JFrame("DisplayLog"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); DefaultTableModel model = new DefaultTableModel(); JTable table = new JTable(model); JProgressBar jpb = new JProgressBar(); f.add(jpb, BorderLayout.NORTH); f.add(new JScrollPane(table)); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); LogWorker lw = new LogWorker(new File(NAME), model); lw.addPropertyChangeListener((PropertyChangeEvent e) -> { SwingWorker.StateValue s = (SwingWorker.StateValue) e.getNewValue(); jpb.setIndeterminate(s.equals(SwingWorker.StateValue.STARTED)); }); lw.execute(); } public static void main(String[] args) { EventQueue.invokeLater(() -> { new DisplayLog().display(); }); } }