将 txt 文件(10 亿行)拆分为块(每个 150 万行)时出现问题,无法确定如何将剩余行写入最后一个文件

Issues with splitting txt file (1 billion rows) into chunks (1.5 mil rows each), unable to figure how to write the remaining rows to last file

我正在学习如何使用文件并决定做大。使用小字典生成 30 GB 的随机文本文件,文件包含 10 亿行,每行是 2 个字符串,由 space.

分隔

我的规格:Ryzen 3600, 16gb 3200 RAM, 512 GB ssd

文本示例:

orthodox_jungle sharp_opera
vertical_referee close_steward
express_wheel intermediate_building
painful_damage similar_fly
violent_justification colourful_opposition

到目前为止,我已经能够将主 txt 文件分成许多临时文件,稍后将对其进行排序并再次合并为一个完整文件(删除重复项)。

主要问题

我遇到了 while ((line = bufferedReader.readLine()) != null) 的情况。由于在计数器达到 150 万(或任何其他拆分范围,100 万或 100k)后文件被严格拆分,因此不会处理和保存剩余文本。在 while 本地范围内创建新的 List 也不允许我在循环结束后将该列表保存到文件中。

进行分块的方法

    public void readFromFileTest(String filePath) throws IOException {

        long start = System.nanoTime();

        String path = "/home/developer/Downloads/tmpfiles/temporaryToSort%d.txt";

        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        String line;
        List<String> listToSort = new ArrayList<>();

        int currentLineCounter = 0;
        int temporaryFileCounter = 0;

        while ((line = bufferedReader.readLine()) != null) {
            if (currentLineCounter == 1500000) {

                String tmpFileLocation = String.format(path, temporaryFileCounter);

                sortAndSaveListToFile(tmpFileLocation, listToSort);

                currentLineCounter = 0;
                temporaryFileCounter++;

                listToSort = new ArrayList<>();
            }

            String[] arrayOfWords = line.split(" ");

            for (String word : arrayOfWords) {
                listToSort.add(word + "\n ");
            }
            // \n is needed, because otherwise my temporary textfile would be considered
            //as one single big String of 50 mb size

            //hence i cant use listToSort.addAll(Arrays.asList(line.split(" ")));

            //listToSort.addAll(Arrays.asList(line.split(" ")));
            currentLineCounter++;
        }

        long time = System.nanoTime() - start;
        System.out.printf("Took %.3f second to read, sort and write to a file%n", time / 1e9);
    }

证明最后一段文字没有处理

从主文件和最后保存的临时文件中读取最后一行我得到不同的文本样本:

public List<String> getLastLinesFromFile(int numLastLineToRead) throws FileNotFoundException {

        File f = new File("/home/developer/Downloads/verylargefile/verylargefile.txt");
//        File f = new File("/home/developer/Downloads/tmpfiles/temporaryToSort665.txt");

        List<String> result = new ArrayList<>();

        try (ReversedLinesFileReader reader = new ReversedLinesFileReader(f, StandardCharsets.UTF_8)) {
            String line = "";
            while ((line = reader.readLine()) != null && result.size() < numLastLineToRead) {
                result.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;

    }

returns 主文件和 tmp 的最后一行:

optimistic_direction senior_ghost
exact_admiration influential_cereal
useful_rice charismatic_syndrome

 main_sweater
 general_review
 current_passion
 afraid_lemon
 stunning_garbage
 presidential_dialect
 low_cathedral
 full_accountant
 crude_survivor

可能的解决方法

简单地计算该文件中的行数,然后用简单的 for 循环替换 while 条件并在 i = lineCount 时保存所有内容。但是在我的机器上计算所有行需要 85 秒,而且很臭。

import org.apache.commons.io.input.ReversedLinesFileReader;

        long lineCount;

        try (Stream<String> stream = Files.lines(Path.of(filePath), StandardCharsets.UTF_8)) {
            lineCount = stream.count();
        }

        System.out.println(lineCount);

最初的计划是使用RandomAccessFile并将其读入byte[]数组,为该数组分配128MB的内存,然后将剩余的数组保存到最后一个文件,但是重新发明太麻烦了bufferReaders readLine() 和文件指针重新定位并寻找换行字节,然后将切碎的字符串片段保存到新的字节数组。

任何其他如何有效分块大文本文件的示例将不胜感激,我知道我的实现是垃圾。

发送帮助:)

我建议看一下来自 AT&T 的 Unix 的 split。 一个简单的解决方案是将您的 while 更改为倒置直到(直到 readline returns null)并将 OR'ed 条件添加到写入块(或不是行)

恕我直言,您只需要(在 while ((line = bufferedReader.readLine()) != null) { /*...*/ } 块之后

if (!listToSort.isEmpty()) {
    String tmpFileLocation = String.format(path, temporaryFileCounter);
    sortAndSaveListToFile(tmpFileLocation, listToSort);
}

或者如果您使用的是 Java 11 之前的 Java 版本:

if (listToSort.size() > 0) {
    String tmpFileLocation = String.format(path, temporaryFileCounter);
    sortAndSaveListToFile(tmpFileLocation, listToSort);
}