繁体   English   中英

读取Java中的大文件

[英]Read large files in Java

我需要一个非常了解Java和内存问题的人的建议。 我有一个大文件(大约1.5GB),我需要将此文件切成许多小文件(例如100个小文件)。

我通常知道如何做到这一点(使用BufferedReader ),但是我想知道您是否对内存有任何建议,或者提示如何更快地做到这一点。

我的文件包含文本,它不是二进制文件,每行大约有20个字符。

为了节省内存,请勿在内存中不必要地存储/复制数据(即,不要将其分配给循环外的变量)。 只要输入输入,就立即处理输出。

是否使用BufferedReader并不重要。 正如一些隐含的暗示那样,它不会花费太多的内存。 最多只能将性能降低几个百分点。 使用NIO时也是如此。 它只会提高可伸缩性,而不会提高内存使用率。 仅当您在同一个文件上运行数百个线程时,它才会变得有趣。

只需遍历文件,在读入时立即将每一行写到其他文件,对行进行计数,如果达到100行,则切换到下一个文件,依此类推。

开球示例:

String encoding = "UTF-8";
int maxlines = 100;
BufferedReader reader = null;
BufferedWriter writer = null;

try {
    reader = new BufferedReader(new InputStreamReader(new FileInputStream("/bigfile.txt"), encoding));
    int count = 0;
    for (String line; (line = reader.readLine()) != null;) {
        if (count++ % maxlines == 0) {
            close(writer);
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("/smallfile" + (count / maxlines) + ".txt"), encoding));
        }
        writer.write(line);
        writer.newLine();
    }
} finally {
    close(writer);
    close(reader);
}

首先,如果您的文件包含二进制数据,则使用BufferedReader将是一个大错误(因为您会将数据转换为String,这是不必要的,并且很容易破坏数据); 您应该改用BufferedInputStream 如果它是文本数据,并且需要按换行符进行拆分,则可以使用BufferedReader (假定文件包含合理长度的行)。

关于内存,如果您使用大小合适的缓冲区应该没有任何问题(我将至少使用1MB的内存来确保HD主要执行顺序读取和写入操作)。

如果发现速度是一个问题,则可以看看java.nio软件包-据说这些软件包比java.io快,

您可以考虑通过FileChannel使用内存映射文件。

一般来说很多大文件的速度更快。 YMMV是性能折衷方案, 可能会使其变慢。

相关答案: Java NIO FileChannel与FileOutputstream的性能/有用性

这是一篇很好的文章: http : //java.sun.com/developer/technicalArticles/Programming/PerfTuning/

总而言之,为了获得出色的性能,您应该:

  1. 避免访问磁盘。
  2. 避免访问基础操作系统。
  3. 避免方法调用。
  4. 避免单独处理字节和字符。

例如,要减少对磁盘的访问,可以使用大缓冲区。 本文介绍了各种方法。

它必须用Java完成吗? 即它需要独立于平台吗? 如果没有,我建议在* nix中使用' split '命令。 如果您确实需要,可以通过Java程序执行此命令。 尽管我还没有进行测试,但我想它的执行速度要比您能想到的任何Java IO实现都要快。

您可以使用比传统输入/输出流更快的java.nio:

http://java.sun.com/javase/6/docs/technotes/guides/io/index.html

 package all.is.well; import java.io.IOException; import java.io.RandomAccessFile; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import junit.framework.TestCase; /** * @author Naresh Bhabat * Following implementation helps to deal with extra large files in java. This program is tested for dealing with 2GB input file. There are some points where extra logic can be added in future. Pleasenote: if we want to deal with binary input file, then instead of reading line,we need to read bytes from read file object. It uses random access file,which is almost like streaming API. * **************************************** Notes regarding executor framework and its readings. Please note :ExecutorService executor = Executors.newFixedThreadPool(10); * for 10 threads:Total time required for reading and writing the text in * :seconds 349.317 * * For 100:Total time required for reading the text and writing : seconds 464.042 * * For 1000 : Total time required for reading and writing text :466.538 * For 10000 Total time required for reading and writing in seconds 479.701 * * */ public class DealWithHugeRecordsinFile extends TestCase { static final String FILEPATH = "C:\\\\springbatch\\\\bigfile1.txt.txt"; static final String FILEPATH_WRITE = "C:\\\\springbatch\\\\writinghere.txt"; static volatile RandomAccessFile fileToWrite; static volatile RandomAccessFile file; static volatile String fileContentsIter; static volatile int position = 0; public static void main(String[] args) throws IOException, InterruptedException { long currentTimeMillis = System.currentTimeMillis(); try { fileToWrite = new RandomAccessFile(FILEPATH_WRITE, "rw");//for random write,independent of thread obstacles file = new RandomAccessFile(FILEPATH, "r");//for random read,independent of thread obstacles seriouslyReadProcessAndWriteAsynch(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } Thread currentThread = Thread.currentThread(); System.out.println(currentThread.getName()); long currentTimeMillis2 = System.currentTimeMillis(); double time_seconds = (currentTimeMillis2 - currentTimeMillis) / 1000.0; System.out.println("Total time required for reading the text in seconds " + time_seconds); } /** * @throws IOException * Something asynchronously serious */ public static void seriouslyReadProcessAndWriteAsynch() throws IOException { ExecutorService executor = Executors.newFixedThreadPool(10);//pls see for explanation in comments section of the class while (true) { String readLine = file.readLine(); if (readLine == null) { break; } Runnable genuineWorker = new Runnable() { @Override public void run() { // do hard processing here in this thread,i have consumed // some time and ignore some exception in write method. writeToFile(FILEPATH_WRITE, readLine); // System.out.println(" :" + // Thread.currentThread().getName()); } }; executor.execute(genuineWorker); } executor.shutdown(); while (!executor.isTerminated()) { } System.out.println("Finished all threads"); file.close(); fileToWrite.close(); } /** * @param filePath * @param data * @param position */ private static void writeToFile(String filePath, String data) { try { // fileToWrite.seek(position); data = "\\n" + data; if (!data.contains("Randomization")) { return; } System.out.println("Let us do something time consuming to make this thread busy"+(position++) + " :" + data); System.out.println("Lets consume through this loop"); int i=1000; while(i>0){ i--; } fileToWrite.write(data.getBytes()); throw new Exception(); } catch (Exception exception) { System.out.println("exception was thrown but still we are able to proceeed further" + " \\n This can be used for marking failure of the records"); //exception.printStackTrace(); } } } 

是。 我还认为,将read()与read(Char [],int init,int end)等参数一起使用是读取此类大文件的更好方法(例如:read(buffer,0,buffer.length))

而且我还遇到了以下问题:对于二进制数据输入流,使用BufferedReader而不是BufferedInputStreamReader会丢失值。 因此,在这种情况下,使用BufferedInputStreamReader会更好。

不要使用没有参数的read。 非常慢 最好读取它以缓冲并快速将其移动到文件中。

使用bufferedInputStream,因为它支持二进制读取。

这就是全部。

除非您不小心读了整个输入文件而不是逐行读取,否则您的主要限制将是磁盘速度。 您可能要尝试从一个包含100行的文件开始,然后将其写入100个不同的文件,每个文件一行一行,并使触发机制在写入当前文件的行数上起作用。 该程序可以轻松扩展以适应您的情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM