如何根据文件大小将CSV文件拆分成多个文件

8
在一个Java项目中,我生成了一个大型的CSV文件(约500 MB),我需要将该文件分割成多个文件,每个文件的大小最多为10 MB。我发现很多帖子都类似,但是没有一个回答我的问题,因为在所有帖子中,Java代码会将原始文件拆分为确切的10 MB文件,并且(显然)截断记录。相反,我需要每条记录都完整无缺,不应该被截断。如果我从原始大型CSV文件复制一条记录到一个生成的文件中,如果我复制该记录,则文件尺寸将溢出10 MB,我应该能够不复制该记录,关闭该文件,创建一个新文件并将该记录复制到新文件中。这可能吗?有人能帮帮我吗?谢谢!
我尝试了这段代码:
File f = new File("/home/luca/Desktop/test/images.csv");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
FileOutputStream out;
String name = f.getName();
int partCounter = 1;
int sizeOfFiles = 10 * 1024 * 1024;// 1MB
byte[] buffer = new byte[sizeOfFiles];
int tmp = 0;
while ((tmp = bis.read(buffer)) > 0) {
 File newFile=new File("/home/luca/Desktop/test/"+name+"."+String.format("%03d", partCounter++));
 newFile.createNewFile();
 out = new FileOutputStream(newFile);
 out.write(buffer,0,tmp);
 out.close();
}

但显然它不起作用。此代码将源文件分割成n个10Mb的文件,截断记录。在我的情况下,我的CSV文件有16列,因此使用上述过程,例如,最后一条记录只有5列填充。其他被截断了。

解决方案 这里是我编写的代码。

FileReader fileReader = new FileReader("/home/luca/Desktop/test/images.csv");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line="";
int fileSize = 0;
BufferedWriter fos = new BufferedWriter(new FileWriter("/home/luca/Desktop/test/images_"+new Date().getTime()+".csv",true));
while((line = bufferedReader.readLine()) != null) {
    if(fileSize + line.getBytes().length > 9.5 * 1024 * 1024){
        fos.flush();
        fos.close();
        fos = new BufferedWriter(new FileWriter("/home/luca/Desktop/test/images_"+new Date().getTime()+".csv",true));
        fos.write(line+"\n");
        fileSize = line.getBytes().length;
    }else{
        fos.write(line+"\n");
        fileSize += line.getBytes().length;
    }
}          
fos.flush();
fos.close();
bufferedReader.close();

这段代码读取一个csv文件并将其分割成n个文件,每个文件最大为10 Mb,并且每个csv行要么完全复制,要么完全不复制。


你已经尝试过哪些代码了吗?如果你只是想让别人为你编写程序,那么你需要雇佣一名程序员。 - nhgrif
这是自由职业者的好规格。 - Maxim Shoustin
没有理由这么粗鲁地回答,而且我不是自由职业者。 - lucavenanzetti
我知道你现在的感受,但你必须明白没有人会根据一个笼统的“我需要这个和那个”的需求开始编写代码。一旦你有了代码并且在某个特定点上卡住了,这里的人们会帮助你。就目前而言,我认为你是否想要在创建非常大的文件时将其拆分成多个文件还不清楚,或者你是想将现有的文件拆分成多个部分。我个人也不明白你是想要将事物拆分成精确的10Mb,还是每个文件都有特定的结构(“记录”)。请提供更多信息和代码。 - JBA
好的,谢谢。我从另一个程序中得到了一个大的CSV文件。因此,只有在生成该文件后,我才能对其进行操作。每个CSV行有16列。期望的结果是一组文件,尺寸尽可能接近10 MB,并且每个文件应仅包含完整记录(每个CSV行应具有16列)。 - lucavenanzetti
3个回答

3
原则上非常简单。
您创建一个10MB的缓冲区(byte []),然后从源中尽可能多地读取字节。 然后从 back 搜索换行符。 从缓冲区开头到换行符的部分=新文件。 您保留已读取的部分,并将其复制到缓冲区的开头(偏移量为0)。 然后重复所有操作,直到没有更多的源。

谢谢。我也添加了解决方案。 - lucavenanzetti

0
这将根据指定的大小,将包括CSV文件在内的任何基于行的文件分割成一个文件(行长度-1)。如果指定了标题行,则会重复标题行(例如用于具有标题行的CSV文件)。
protected void processDocument(File inFile, long maxFileSize, boolean containsHeaderRow) {       
    if (maxFileSize > 0 && infile.length() > maxFileSize) {
        FileReader fileReader = new FileReader(inFile);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        try {
            byte[] headerRow = new byte[0];
            if (containsHeaderRow) {
                try {
                    String headerLine = bufferedReader.readLine();
                    if (headerLine != null) {
                        headerRow = (headerLine + "\n").getBytes();
                    }
                } catch (IOException e1) {
                    throw new Exception("Failed to read header row from input file.", e1);
                }
            }
            long headerRowByteCount = headerRow.length;
            if (maxFileSize < headerRowByteCount) {
                // Would just write header repeatedly so throw error
                throw new Exception("Split file size is less than the header row size.");
            }
            int fileCount = 0;
            boolean notEof = true;
            while (notEof) {
                fileCount += 1;
                long fileSize = 0;
                // create a new file with same path but appended count
                String newFilename = inFile.getAbsolutePath() |+ "-" + fileCount;
                File outFile = new File(newFilename);
                BufferedOutputStream fos = null;
                try {
                    try {
                        fos = new BufferedOutputStream(new FileOutputStream(outFile));
                    } catch (IOException e) {
                        throw new Exception("Failed to initialise output file for file splitting on file " + fileCount, e);
                    }
                    if (containsHeaderRow) {
                        try {
                            fos.write(headerRow);
                        } catch (IOException e) {
                            throw new Exception("Failed to write header row to output file for file splitting on file " + fileCount, e);
                        }
                        fileSize += headerRowByteCount;
                    }
                    while (fileSize < maxFileSize) {
                        String line = null;
                        try {
                            line = bufferedReader.readLine();
                        } catch (IOException e) {
                            throw new Exception("Failed to write output file for file splitting on file " + fileCount, e);
                        }
                        if (line == null) {
                            notEof = false;
                            break;
                        }
                        byte[] lineBytes = (line + "\n").getBytes();
                        fos.write(lineBytes);
                        fileSize += lineBytes.length;
                    }
                    fos.flush();
                    fos.close();
                    processDocument(outFile); 
                } catch (IOException e) {
                    throw new Exception("Failed to write output file for file splitting on file number" + fileCount, e);
                } finally {
                    try {
                        if (fos != null) {
                            fos.close();
                        }
                    } catch (IOException e) {
                    }
                }
            }
        } finally {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                throw new Exception("Failed to close reader for input file.", e);
            }
        }

    } else {
        processDocument(inFile); 
    }
}

0

使用以下命令:split -a 3 -b 100m -d filename.tar.gz newfilename



这将在每个分割文件的末尾截断各个记录。 - eaubin

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