web-dev-qa-db-de.com

Lesen Sie große Dateien in Java

Ich brauche den Rat von jemandem, der Java sehr gut kennt und die Speicherprobleme. Ich habe eine große Datei (etwa 1,5 GB) und ich muss diese Datei in viele kleine Dateien (zum Beispiel 100 kleine Dateien) schneiden.

Ich weiß im Allgemeinen, wie es geht (mit einer BufferedReader), aber ich würde gerne wissen, ob Sie irgendwelche Ratschläge bezüglich des Speichers haben oder Tipps, wie Sie dies schneller erledigen können.

Meine Datei enthält Text, es ist nicht binär und ich habe ungefähr 20 Zeichen pro Zeile.

56
CC.

Erstens, wenn Ihre Datei binäre Daten enthält, wäre die Verwendung von BufferedReader ein großer Fehler (da Sie die Daten in String konvertieren würden, was unnötig ist und die Daten leicht beschädigen kann). Sie sollten stattdessen eine BufferedInputStream verwenden. Wenn es sich um Textdaten handelt, die Sie entlang von Zeilenumbrüchen aufteilen müssen, ist die Verwendung von BufferedReader in Ordnung (vorausgesetzt, die Datei enthält Zeilen mit sinnvoller Länge).

In Bezug auf den Arbeitsspeicher sollte es kein Problem geben, wenn Sie einen Puffer mit angemessener Größe verwenden (ich würde mindestens 1 MB verwenden, um sicherzustellen, dass das HD hauptsächlich sequentiell liest und schreibt).

Wenn sich die Geschwindigkeit als problematisch herausstellt, können Sie sich die Java.nio-Pakete anschauen - diese sind vermutlich schneller als Java.io

27

Um Speicherplatz zu sparen, speichern/duplizieren Sie die Daten nicht unnötig (d. H. Weisen Sie sie nicht Variablen außerhalb der Schleife zu). Verarbeiten Sie einfach die Ausgabe sofort , sobald die Eingabe eingeht.

Es ist wirklich egal, ob Sie BufferedReader verwenden oder nicht. Es wird nicht viel mehr Speicher kosten, wie einige implizit zu vermuten scheinen. Im Höchstfall wird es nur ein paar Prozent von der Leistung sein. Gleiches gilt für die Verwendung von NIO. Es wird nur die Skalierbarkeit verbessert, nicht die Speichernutzung. Es wird nur interessant, wenn Sie hunderte von Threads in derselben Datei ausführen.

Durchlaufen Sie einfach die Datei, schreiben Sie beim Einlesen jede Zeile sofort in eine andere Datei, zählen Sie die Zeilen und wenn sie 100 erreicht, wechseln Sie zur nächsten Datei usw.

Kickoff-Beispiel:

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

Sie können die Verwendung von Dateien mit dem Speicherinhalt über FileChannel s in Betracht ziehen.

Im Allgemeinen ist viel schneller für große Dateien. Es gibt Leistungsabwägungen, die könnte langsamer machen, so YMMV.

Zugehörige Antwort: Java NIO FileChannel im Vergleich zu FileOutputstream - Leistung/Nützlichkeit

12
Ryan Emerle

Dies ist ein sehr guter Artikel: http://Java.Sun.com/developer/technicalArticles/Programming/PerfTuning/

Zusammenfassend sollten Sie für eine großartige Leistung Folgendes tun:

  1. Vermeiden Sie den Zugriff auf die Festplatte.
  2. Vermeiden Sie den Zugriff auf das zugrunde liegende Betriebssystem.
  3. Vermeiden Sie Methodenaufrufe.
  4. Vermeiden Sie die individuelle Verarbeitung von Bytes und Zeichen.

Um beispielsweise den Zugriff auf die Festplatte zu reduzieren, können Sie einen großen Puffer verwenden. Der Artikel beschreibt verschiedene Ansätze.

4
b.roth

Muss es in Java gemacht werden? Das heißt Muss es plattformunabhängig sein? Wenn nicht, würde ich vorschlagen, den Befehl ' split ' in * nix zu verwenden. Wenn Sie es wirklich wollten, können Sie diesen Befehl über Ihr Java-Programm ausführen. Während ich noch nicht getestet habe, kann ich mir vorstellen, dass die Java-Implementierung schneller ist als die Implementierung von Java IO.

3
Mike

Sie können Java.nio verwenden, das schneller als klassischer Eingabe-/Ausgabestrom ist:

http://Java.Sun.com/javase/6/docs/technotes/guides/io/index.html

1
Kartoch

Ja. Ich denke auch, dass die Verwendung von read () mit Argumenten wie read (Char [], int init, int end) eine bessere Möglichkeit ist, eine solche große Datei zu lesen .Länge))

Ich habe auch das Problem erfahren, dass Werte für die Verwendung des BufferedReader anstelle von BufferedInputStreamReader für einen binären Dateneingangsstrom fehlen. Die Verwendung des BufferedInputStreamReader ist in diesem Fall also viel besser.

1
Namalak

Wenn Sie nicht versehentlich die gesamte Eingabedatei einlesen, anstatt sie zeilenweise zu lesen, ist Ihre Hauptbeschränkung die Festplattengeschwindigkeit. Sie können versuchen, mit einer Datei mit 100 Zeilen zu beginnen und sie in jeweils 100 Zeilen in jeweils eine Zeile zu schreiben, damit der Auslösemechanismus auf die Anzahl der in die aktuelle Datei geschriebenen Zeilen angewendet wird. Dieses Programm kann leicht an Ihre Situation angepasst werden.

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

		}

	}
}

0
RAM

Verwenden Sie read nicht ohne Argumente . Es ist sehr langsam.

Verwenden Sie bufferedInputStream, da es das binäre Lesen unterstützt.

Und das ist alles.

0
oneat