如何读取 Java 中的大文件(单个连续字符串)?

How to read large files (a single continuous string) in Java?

我正在尝试读取一个非常大的文件 (~2GB)。内容是一个带有句子的连续字符串(我想根据'.'来拆分它们)。无论我如何尝试,我最终都会遇到 Outofmemoryerror。

    BufferedReader in = new BufferedReader(new FileReader("a.txt"));
    String read = null;
    int i = 0;
    while((read = in.readLine())!=null) {
        String[] splitted = read.split("\.");
        for (String part: splitted) {
            i+=1;
            users.add(new User(i,part));
            repository.saveAll(users);
        }
    }

另外,

inputStream = new FileInputStream(path);
    sc = new Scanner(inputStream, "UTF-8");
    while (sc.hasNextLine()) {
        String line = sc.nextLine();
        // System.out.println(line);
    }
    // note that Scanner suppresses exceptions
    if (sc.ioException() != null) {
        throw sc.ioException();
    }

文件内容(由随机词组成,10个词后有句号):

fmfbqi .xcdqnjqln kvjhw pexrbunnr cgvrqlr fpaczdegnb puqzjdbp gcfxne jawml aaiwwmo ugzoxn .opjc fmfbqi .xcdqnjqln kvjhw pexrbunnr cgvrqlr fpaczdegnb puqzjdbp gcfxne jawml aaiwwmo ugzoxn .opjc  (so on)

请帮忙!

首先,根据对您问题的评论,正如 Joachim Sauer 所说:

If there are no newlines, then there is only a single line and thus only one line number.

所以你的用例充其量是有问题的。

让我们过去,假设可能有换行符 - 或者更好的是,假设您要拆分的 . 字符是换行符伪替换。

Scanner 在这里不是一个坏方法,尽管还有其他方法。由于您提供了 Scanner,让我们继续,但您要确保将其包装在 BufferedReader 周围。您显然没有太多内存,并且 BufferedReader 允许您读取 'chunks' 文件,由 BufferedReader 缓冲,同时利用 [=13] 的功能=] 作为调用者,您完全不知道正在发生缓冲:

Scanner sc = new Scanner(new BufferedReader(new FileReader(new File("a.txt")), 10*1024));

这基本上是在让 Scanner 按您的预期运行,但允许您一次缓冲 10MB,从而最大限度地减少内存占用。现在,你只要继续打电话

sc.useDelimiter("\.");
for(int i = 0; sc.hasNext(); i++) {
    String psudeoLine = sc.next();
    //store line 'i' in your database for this psudeo-line
    //DO NOT store psudeoLine anywhere else - you don't have memory for it
}

由于您没有足够的内存,迭代(和重新迭代)的明确事项是在读取文件后不要将文件的任何部分存储在 JVM 的堆空间中。阅读它,根据需要使用它,并允许将其标记为 JVM 垃圾收集。在你的情况下,你提到你想将伪行存储在数据库中,所以你想读取伪行,将它存储在数据库中,然后丢弃它。

这里还有其他事情需要指出,例如配置 JVM 参数,但我什至不愿提及它,因为仅仅将 JVM 内存设置得高也是一个坏主意——另一种蛮力方法。将 JVM 内存最大堆大小设置得更高并没有错,但是如果您仍在学习如何编写软件,学习内存管理会更好。当你进入职业发展后,你会遇到更少的麻烦。

此外,我提到了 ScannerBufferedReader,因为您在问题中提到了这一点,但我认为查看 deHaar 指出的 java.nio.file.Path.lines() 也是一个好主意。这基本上与我明确列出的代码做同样的事情,但需要注意的是它仍然一次只做 1 行而不能改变你正在 'splitting' 的内容。因此,如果您的文本文件中只有 1 行,这仍然会给您带来问题,并且您仍然需要像扫描仪这样的东西来将行分段。