Leia arquivos grandes em Java

Eu preciso do conselho de alguém que conhece muito bem o Java e os problemas de memory. Eu tenho um arquivo grande (algo como 1,5 GB) e eu preciso cortar esse arquivo em muitos (100 pequenos arquivos, por exemplo) arquivos menores.

Eu geralmente sei como fazê-lo (usando um BufferedReader ), mas eu gostaria de saber se você tem algum conselho sobre a memory, ou dicas sobre como fazê-lo mais rápido.

Meu arquivo contém texto, não é binário e tenho cerca de 20 caracteres por linha.

Primeiro, se o seu arquivo contiver dados binários, usar BufferedReader seria um grande erro (porque você converteria os dados para String, o que é desnecessário e poderia facilmente corromper os dados); você deve usar um BufferedInputStream lugar. Se forem dados de texto e você precisar dividi-los em quebras de linha, usar BufferedReader é OK (supondo que o arquivo contenha linhas de tamanho razoável).

Em relação à memory, não deve haver nenhum problema se você usar um buffer de tamanho decente (eu usaria pelo menos 1 MB para garantir que o HD esteja basicamente lendo e escrevendo em sequência).

Se a velocidade for um problema, você pode dar uma olhada nos pacotes java.nio – supostamente mais rápidos que java.io ,

Para economizar memory, não armazene / duplique desnecessariamente os dados na memory (ou seja, não os atribua a variables ​​fora do loop). Basta processar a saída imediatamente assim que a input entrar.

Realmente não importa se você está usando o BufferedReader ou não. Não custará significativamente muito mais memory, como algumas implicitamente parecem sugerir. No máximo, atingirá apenas alguns% do desempenho. O mesmo se aplica ao uso do NIO. Isso só melhorará a escalabilidade, não o uso da memory. Ele só ficará interessante quando você tiver centenas de threads em execução no mesmo arquivo.

Apenas percorra o arquivo, escreva cada linha imediatamente em outro arquivo enquanto você lê, conte as linhas e, se atingir 100, mude para o próximo arquivo, etc.

Exemplo de pontapé inicial:

 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); } 

Você pode considerar o uso de arquivos mapeados na memory, via FileChannel s.

Geralmente muito mais rápido para arquivos grandes. Há trade-offs de desempenho que poderiam torná-lo mais lento, então YMMV.

Resposta relacionada: Java NIO FileChannel versus desempenho / utilidade do FileOutputstream

Este é um artigo muito bom: http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/

Em resumo, para um ótimo desempenho, você deve:

  1. Evite acessar o disco.
  2. Evite acessar o sistema operacional subjacente.
  3. Evite chamadas de método.
  4. Evite processar bytes e caracteres individualmente.

Por exemplo, para reduzir o access ao disco, você pode usar um buffer grande. O artigo descreve várias abordagens.

Isso precisa ser feito em Java? Ou seja, precisa ser independente de plataforma? Se não, sugiro usar o comando ‘ split ‘ em * nix. Se você realmente quisesse, você poderia executar este comando através do seu programa java. Embora eu não tenha testado, imagino que ele tenha um desempenho mais rápido do que qualquer implementação de Java IO que você pudesse criar.

Você pode usar o java.nio, que é mais rápido que o stream clássico de input / saída:

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

Sim. Eu também acho que usar read () com argumentos como read (Char [], intin, int end) é uma maneira melhor de ler um arquivo tão grande (por exemplo: read (buffer, 0, buffer.length))

E também experimentei o problema de valores ausentes do uso do BufferedReader em vez de BufferedInputStreamReader para um stream de input de dados binários. Então, usar o BufferedInputStreamReader é muito melhor nesse caso.

Não use ler sem argumentos. É muito lento. Melhor lê-lo para buffer e mova-o para o arquivo rapidamente.

Use bufferedInputStream porque ele suporta leitura binária.

E é tudo.

A menos que você acidentalmente leia todo o arquivo de input em vez de lê-lo linha por linha, sua principal limitação será a velocidade do disco. Você pode tentar iniciar com um arquivo contendo 100 linhas e gravá-lo em 100 arquivos diferentes em uma linha e fazer com que o mecanismo de acionamento funcione no número de linhas gravadas no arquivo atual. Esse programa será facilmente escalável para sua situação.

 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(); } } }