如何在Swing中读取和显示大型文本文件?

3
这可能听起来有点复杂,我会尝试简化我的问题。 我正在开发的程序可以使用 JTextArea 从/向文件读写。当文件相当大时,将数据从该文件读取到文本区域确实需要很长时间。例如,我有一个文件,目前有40,000行文本,每行大约50个字符;而且有些行会换行。有很多文本内容,从文件中读取需要花费更多的时间。
目前,我正在使用标准的读取方法,利用 JTextArea 组件包含的 BufferedReader 实例。我想做的是在屏幕上加载一定量的文本到 JTextArea 上,并在后台使用单独的线程加载屏幕外的其余文本。
使用 InputStream 并将每个字符写入数组,然后将字符写入 JTextArea 是否足够?或者应该采用不同的方法?我想实现一种快速和有效的读取方法。

(顺带一提,对于今天的硬件来说,2MB的文件并不算很“大”——也就是说,它运行得太慢了吗?对于真正的大文件,有更好的方法。) - user2864740
好的,实际上它大小为4.20 mb,但我同意。然而,加载时间仍然比我喜欢的要长。 - Frizinator
使用BufferedReader(包装InputStream),顺序IO应该是“几乎即时”的。计算加载(IO)和加载(UI)的时间,看看哪个引入了不必要的延迟。 - user2864740
我认为这可能是最好的方法 - sgbj
3
几乎可以确定问题不在读取文件上,而是在尝试让JTextArea处理数据时出现了问题。您可以进行手动分页,仅将某个特定子集的数据设置到jtextarea中。 - Kylar
显示剩余2条评论
1个回答

7

现在有两个紧急的问题需要处理。

首先,需要以逐步更新UI的方式读取文件,而不会导致不可接受的延迟。

其次,需要确保 JTextArea 能够真正处理这么多数据...

相对来说,第一个问题很容易解决。您需要确保在读取文件时没有阻塞事件分派线程,并且只从事件分派线程上下文中更新JTextArea。为此,SwingWorker 是一个出色的选择,例如...

public class FileReaderWorker extends SwingWorker<List<String>, String> {

    private File file;
    private JTextArea ta;

    public FileReaderWorker(File file, JTextArea ta) {
        this.file = file;
        this.ta = ta;
    }

    public File getFile() {
        return file;
    }

    public JTextArea getTextArea() {
        return ta;
    }

    @Override
    protected List<String> doInBackground() throws Exception {
        List<String> contents = new ArrayList<>(256);
        try (BufferedReader br = new BufferedReader(new FileReader(getFile()))) {
            String text = null;
            while ((text = br.readLine()) != null) {
                // You will want to deal with adding back in the new line characters
                // here if that is important to you...
                contents.add(text);
                publish(text);
            }
        }
        return contents;
    }

    @Override
    protected void done() {
        try {
            get();
        } catch (InterruptedException | ExecutionException ex) {
            ex.printStackTrace();
            // Handle exception here...
        }
    }

    @Override
    protected void process(List<String> chunks) {
        JTextArea ta = getTextArea();
        for (String text : chunks) {
            ta.append(text);
        }
    }

}

请查看Swing中的并发工作线程和SwingWorker以获取更多详细信息。
PS- 您不需要使用List来存储内容,我只是举例说明...
第二个问题要复杂得多,需要进行一些额外的测试以确保它实际上是一个问题,但总的来说,大约超过1mb的内容往往会导致问题...
为此,您需要能够管理JScrollPane,能够向前和向后请求文件中的文本块,并尝试有效地“欺骗”过程(这样您只加载需要的文本,但仍然可以使其看起来像您在JTextArea中加载了所有文本)...
您还可以查看FileChannel,它提供了更多功能,包括内存映射,首先,请查看读取、写入和创建文件
您还可以考虑使用高度优化用于显示大量数据的JListJTable。这方面有一些限制,因为有固定行高的期望,当更改时(到动态行高),可能会影响性能,但可能是一个合适的替代方案...

1
我会尝试使用JListJTable作为视图,以利用轻量级渲染。 - trashgod
1
@trashgod 对于查看来说,完全可以。但是如果OP希望能够编辑这些值,那就会变得有点复杂,不过并非不可能... - MadProgrammer
好的观点;默认的JTable编辑器已经足够了,但是写回可能会很繁琐。 - trashgod
还有固定和动态行高的问题,使用固定行高时,组件非常优化,而动态行高意味着组件需要测试每一行才能计算出组件的高度,如果小心处理,更新可以做得很好,但这是需要考虑的一个因素... - MadProgrammer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接