如何在 Java 中按行将 CSV 文件拆分为不同的 CSV 文件?
How can I split a CSV file into different CSV files by line in Java?
我有一个 class 可以读取 CSV 文件,但是当文件很大时,程序会抛出 Java heap size
错误,所以我需要将该文件拆分成多个部分并将行传输到其他文件根据行大小的文件。
例如;
我有一个 500 000 行的文件,我将它按 100 000 行分成 5 个文件。所以我有 5 个文件,包含 100 000 行,以便我可以阅读它们。
我找不到这样做的方法,所以如果我能看到示例代码行就好了。
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();
}
}
}
如果您使用 Linux 并且可以先通过脚本 运行 CSV,那么您可以使用 "split":
$ split -l 100000 big.csv small-
这会生成名为 small-aa、small-ab、small-ac 的文件...如果需要,将这些文件重命名为 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 reader 模块 运行 内存不足的原因是因为它要么在拆分之前将整个文件读入内存,或者它正在这样做并将处理后的输出保存在内存中。为了使您的代码更具可移植性和通用性 运行nable,您应该考虑一次处理一行 - 并自己逐行拆分输入。 (来自 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();
}
}
我在 FileWriter 或 BufferedWriter 之上选择了 PrintWriter,因为它会自动打印相关的换行符 - 我认为它是缓冲的......我已经 20 年没有在 Java 中写过任何东西,所以我打赌你可以改进以上。
我有一个 class 可以读取 CSV 文件,但是当文件很大时,程序会抛出 Java heap size
错误,所以我需要将该文件拆分成多个部分并将行传输到其他文件根据行大小的文件。
例如; 我有一个 500 000 行的文件,我将它按 100 000 行分成 5 个文件。所以我有 5 个文件,包含 100 000 行,以便我可以阅读它们。
我找不到这样做的方法,所以如果我能看到示例代码行就好了。
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();
}
}
}
如果您使用 Linux 并且可以先通过脚本 运行 CSV,那么您可以使用 "split":
$ split -l 100000 big.csv small-
这会生成名为 small-aa、small-ab、small-ac 的文件...如果需要,将这些文件重命名为 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 reader 模块 运行 内存不足的原因是因为它要么在拆分之前将整个文件读入内存,或者它正在这样做并将处理后的输出保存在内存中。为了使您的代码更具可移植性和通用性 运行nable,您应该考虑一次处理一行 - 并自己逐行拆分输入。 (来自 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();
}
}
我在 FileWriter 或 BufferedWriter 之上选择了 PrintWriter,因为它会自动打印相关的换行符 - 我认为它是缓冲的......我已经 20 年没有在 Java 中写过任何东西,所以我打赌你可以改进以上。