Как я могу разбить файл CSV на разные файлы CSV построчно в Java? - PullRequest
0 голосов
/ 17 марта 2020

У меня есть класс, который читает файл CSV, но когда размер файла велик, программа выдает ошибку Java heap size, поэтому мне нужно разбить этот файл на части и перенести строки в другие файлы в соответствии с размером строки.

Например; У меня есть файл 500 000 строк, и я делю его на 5 файлов на 100 000 строк. Итак, у меня есть 5 файлов, состоящих из 100 000 строк, чтобы я мог их прочитать.

Я не смог найти способ сделать это, поэтому было бы неплохо, если бы я увидел примеры строк кода.

Ответы [ 2 ]

1 голос
/ 17 марта 2020
public static void splitLargeFile(final String fileName, 
                                   final String extension, 
                                   final int maxLines,
                                   final boolean deleteOriginalFile) {

    try (Scanner s = new Scanner(new FileReader(String.format("%s.%s", fileName, extension)))) {
        int file = 0;
        int cnt = 0;
        BufferedWriter writer = new BufferedWriter(new FileWriter(String.format("%s_%d.%s", fileName, file, extension)));

        while (s.hasNext()) {
            writer.write(s.next() + System.lineSeparator());
            if (++cnt == maxLines && s.hasNext()) {
                writer.close();
                writer = new BufferedWriter(new  FileWriter(String.format("%s_%d.%s", fileName, ++file, extension)));
                cnt = 0;
            }
        }
        writer.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

    if (deleteOriginalFile) {
        try {
            File f = new File(String.format("%s.%s", fileName, extension));
            f.delete();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
0 голосов
/ 17 марта 2020

Если вы используете Linux, и вы можете сначала запустить CSV через скрипт, то вы можете использовать «split»:

$ split -l 100000 big.csv small-

Это создает файлы с именем small-aa, small-ab , small-a c ... Чтобы переименовать их в csv, если необходимо:

$ for a in small-*; do 
    mv $a $a.csv;                # rename split files to .csv 
    java MyCSVProcessor $a.csv;  # or just process them anyways 
done

Попробуйте это для дополнительных опций:

$ split -h

-a –suffix-length=N use suffixes of length N (default 2)
-b –bytes=SIZE put SIZE bytes per output file
-C –line-bytes=SIZE put at most SIZE bytes of lines per output file
-d –numeric-suffixes use numeric suffixes instead of alphabetic
-l –lines=NUMBER put NUMBER lines per output file

Это, однако, плохое смягчение для вашего проблема - причина того, что вашему модулю чтения CSV не хватает памяти, заключается в том, что он либо читает весь файл в память перед его разбиением, либо делает это и сохраняет обработанный вывод в памяти. Чтобы сделать ваш код более переносимым и универсально работоспособным, вам следует рассмотреть возможность обработки по одной строке за раз - и разбивать ввод самостоятельно, построчно. (С https://stackabuse.com/reading-and-writing-csvs-in-java/)

BufferedReader csvReader = new BufferedReader(new FileReader(pathToCsv));
while ((row = csvReader.readLine()) != null) {
    String[] data = row.split(",");
    // do something with the data
}
csvReader.close();

Предостережение с приведенным выше кодом заключается в том, что запятые в кавычках будут просто рассматриваться как новые столбцы - вам придется добавить дополнительную обработку, если ваши данные CSV содержит кавычки.

Конечно, если вы действительно хотите использовать существующий код и просто хотите разделить файл, вы можете изменить приведенное выше:

import java.io.*;

public class split {

    static String CSVFile="test.csv";
    static String row;
    static BufferedReader csvReader;
    static PrintWriter csvWriter;

    public static void main(String[] args) throws IOException {   

    csvReader = new BufferedReader(new FileReader(CSVFile));

    int line = 0;
    while ((row = csvReader.readLine()) != null) {
       if (line % 100000 == 0) {  // maximum lines per file
          if (line>0) { csvWriter.close(); }
          csvWriter = new PrintWriter("cut-"+Integer.toString(line)+CSVFile);
       }
       csvWriter.println(row);
        // String[] data = row.split(",");
        // do something with the data
       line++;
    }
    csvWriter.close();
    csvReader.close();

    }
}

Я выбрал PrintWriter вместо FileWriter или BufferedWriter, потому что он автоматически печатает соответствующие новые строки - и я предполагаю, что он буферизован ... Я ничего не писал за Java за 20 лет, так что держу пари, что вы можете улучшить вышеперечисленное.

...