Lee archivos grandes en Java

Necesito el consejo de alguien que conoce muy bien Java y los problemas de memoria. Tengo un archivo grande (algo así como 1.5GB) y necesito cortar este archivo en muchos (100 archivos pequeños, por ejemplo) archivos más pequeños.

Generalmente sé cómo hacerlo (usando un BufferedReader ), pero me gustaría saber si tienes algún consejo con respecto a la memoria o consejos sobre cómo hacerlo más rápido.

Mi archivo contiene texto, no es binario y tengo aproximadamente 20 caracteres por línea.

Primero, si su archivo contiene datos binarios, entonces usar BufferedReader sería un gran error (porque estaría convirtiendo los datos a String, lo cual es innecesario y podría fácilmente corromper los datos); deberías usar un BufferedInputStream lugar. Si se trata de datos de texto y necesita dividirlos en linebreaks, entonces usar BufferedReader es correcto (suponiendo que el archivo contenga líneas de una longitud razonable).

Con respecto a la memoria, no debería haber ningún problema si usa un búfer de tamaño decente (usaría al menos 1MB para asegurarme de que la HD está haciendo principalmente lectura y escritura secuencial).

Si la velocidad resulta ser un problema, podrías echar un vistazo a los paquetes de java.nio , supuestamente más rápidos que java.io ,

Para ahorrar memoria, no almacene / duplique innecesariamente los datos en la memoria (es decir, no los asigne a variables fuera del ciclo). Simplemente procese la salida inmediatamente tan pronto como ingrese la entrada.

Realmente no importa si estás usando BufferedReader o no. No costará mucha más memoria como algunos parecen sugerir implícitamente. En el más alto solo alcanzará un% de rendimiento. Lo mismo aplica al usar NIO. Solo mejorará la escalabilidad, no el uso de memoria. Solo será interesante cuando tengas cientos de subprocesos ejecutándose en el mismo archivo.

Simplemente recorra el archivo, escriba cada línea inmediatamente en otro archivo a medida que lee, cuente las líneas y si llega a 100, luego cambie al siguiente archivo, etcétera.

Ejemplo de lanzamiento:

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

Puede considerar el uso de archivos mapeados en memoria, a través de FileChannel s.

Generalmente mucho más rápido para archivos grandes. Hay intercambios de rendimiento que podrían hacerlo más lento, por lo que YMMV.

Respuesta relacionada: Java NIO FileChannel versus FileOutputstream performance / usefulness

Este es un artículo muy bueno: http://java.sun.com/developer/technicalArticles/Programming/PerfTuning/

En resumen, para un gran rendimiento, debe:

  1. Evite acceder al disco.
  2. Evite acceder al sistema operativo subyacente.
  3. Evita las llamadas a los métodos.
  4. Evite procesar bytes y caracteres individualmente.

Por ejemplo, para reducir el acceso al disco, puede usar un gran buffer. El artículo describe varios enfoques.

¿Tiene que hacerse en Java? Es decir, ¿necesita ser independiente de la plataforma? Si no, sugeriría usar el comando ‘ dividir ‘ en * nix. Si realmente lo deseaba, podría ejecutar este comando a través de su progtwig Java. Si bien no lo he probado, imagino que funcionará más rápido que cualquier implementación de Java IO que se te ocurra.

Puede usar java.nio, que es más rápido que el flujo clásico de entrada / salida:

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

Sí. También creo que usar read () con argumentos como read (Char [], int init, int end) es una mejor manera de leer un archivo tan grande (Ej: read (buffer, 0, buffer.length))

Y también experimenté el problema de los valores perdidos de usar el BufferedReader en lugar de BufferedInputStreamReader para una stream de entrada de datos binarios. Entonces, usar BufferedInputStreamReader es mucho mejor en este caso similar.

No use leer sin argumentos. Es muy lento. Mejor leerlo para almacenar y moverlo rápidamente al archivo.

Utilice bufferedInputStream porque admite lectura binaria.

Y es todo.

A menos que accidentalmente lea todo el archivo de entrada en lugar de leerlo línea por línea, su principal limitación será la velocidad del disco. Puede intentar comenzar con un archivo que contenga 100 líneas y escribirlo en 100 archivos diferentes, una línea en cada uno, y hacer que el mecanismo de activación funcione en el número de líneas escritas en el archivo actual. Ese progtwig será fácilmente escalable para su situación.

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