Java - Sektion aus großer Datei extrahieren

Festplatte

PCGH-Community-Veteran(in)
Java - Sektion aus großer Datei extrahieren

Moin, ich beschäftige mich seit etwa 2 Tagen mit Java und möchte nun ein kleines Programm schreiben, das mir aus einer großen Datei eine Sektion zwischen zwei bestimmten Zeilen raussucht und diese in eine neue Datei schreibt. Die Quell-Datei ist etwa 900 MB groß. Aus dieser möchte ich nun den Text zwischen "/start xxxx" dem nächsten "/start yyyy" raussuchen und diesen in eine neue Datei schreiben. Der String xxxx ist vorher vom Nutzer festgelegt, der String yyyy wird nicht vom Nutzer definiert und soll hier jetzt einfach das nächste "/start" symbolisieren. Hier ein Beispiel:

Code:
blablablablabla

/start baum

blubblubblubblub

/start banane

blablablablabla

Die vom Programm erstellte Datei sollte also den Inhalt "blubblubblubblub" haben. Ich habe bis jetzt schon ein kleines Programm geschrieben, das eine Datei einlesen, in eine ArrayList kopieren und wieder als Datei schreiben kann:

Code:
public class Test
{
	public static void main(String[] args)
	{
		if (args.length == 0)
		{
			userInput();
		}
		else
		{
			if (args.length == 2)
			{
				String sourceNameArg = args[0];
				String databaseNameArg = args[1];
				readFile(sourceNameArg, databaseNameArg);
			}
		}
	}

	[..userInput..]

	public static void readFile(String sourceName, String databaseName)
	{
		File sourceFile = new File(sourceName);

		try (BufferedReader input = new BufferedReader(new FileReader(sourceFile)); Scanner scanner = new Scanner(input))
		{
			ArrayList<String> textArray = new ArrayList<>();
			long startTime = System.currentTimeMillis();

			while (scanner.hasNextLine())
			{
				String line = scanner.nextLine();
				textArray.add(line);
			}

			System.out.println("Elapsed: " + (System.currentTimeMillis() - startTime) + " ms");
			writeFile(textArray, databaseName);
		}
		catch (IOException e)
		{
			System.out.println("'" + sourceName + "' not found.");
			userInput();
		}
	}

	public static void writeFile(ArrayList<String> text, String targetName)
	{
		try (PrintWriter writer = new PrintWriter(new FileWriter(targetName + ".dmp")))
		{
			Iterator<String> iterator = text.iterator();

			while (iterator.hasNext())
			{
				Object o = iterator.next();
				writer.println(o);
			}
		}
		catch (IOException e)
		{
			System.out.print("Something went wrong.");
		}
	}
}

Bei kleinen Dateien kein Problem, bei einer 800 MB Datei stürzt der Spaß während dem Einlesen mit einem OutOfMemory ab. Klar, 800 MB in ein Array zu hauen braucht nun mal ordentlich Speicher. Nun zu meinen Fragen:

1) Ist es grundsätzlich richtig, den Text mittels BufferedReader(new FileReader(sourceFile)) und einem Scanner einzulesen oder geht das noch besser?

2) Brauche ich für das Extrahieren einer bestimmten Sektion überhaupt das Array oder kann ich die neue Datei direkt schreiben? Wenn ja, wie?

3) Wie genau kann ich das eigentliche Extrahieren der Sektion realisieren, vom "/start meinString" bis hin zum nächsten "/start"?
 
Zuletzt bearbeitet:
AW: Java - Sektion aus großer Datei extrahieren

So oder so ähnlich könnte man es machen:

Code:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Extractor {	

	public static void main(String[] args) {
		if (args.length != 3) {
			System.out.println("Ungültige Anzahl an Argumenten.");
			return;
		}

		String source = args[0];
		String destination = args[1];
		String section = args[2];

		ExtractSection(source, destination, section);
	}

	private static void ExtractSection(String source, String destination, String section) {
		try (BufferedReader reader = new BufferedReader(new FileReader(source));
				PrintWriter writer = new PrintWriter(new FileWriter(destination))) {
			SkipToSection(section, reader);
			CopySectionData(reader, writer);
		} catch (FileNotFoundException e) {
			System.out.println("Quelldatei nicht gefunden.");
		} catch (IOException e) {
			System.out.println(e.getMessage());
		}
	}

	private static void SkipToSection(String section, BufferedReader reader) throws IOException {
		String line = reader.readLine();
		String sectionMarker = "/start " + section;

		while (line != null && !line.startsWith(sectionMarker))
			line = reader.readLine();
	}

	private static void CopySectionData(BufferedReader source,	PrintWriter destination) throws IOException {
		String line = source.readLine();
		
		while (line != null && !line.startsWith("/start")) {
			destination.println(line);
			line = source.readLine();
		}
	}

}
 
AW: Java - Sektion aus großer Datei extrahieren

Hi,

eine kleine Quick-and-Dirty Lösung:

Code:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class Test
{
    public static void main(final String[] args) throws IOException
    {
        final BufferedReader reader = getBufferedReader("file");
        final BufferedWriter writer = getBufferedWriter("file");

        copyContent(reader, writer, "marker");

        writer.flush();
        writer.close();
        reader.close();
    }

    private static BufferedReader getBufferedReader(final String sourceName) throws FileNotFoundException {
        return new BufferedReader(new InputStreamReader(new FileInputStream(sourceName)));
    }

    private static BufferedWriter getBufferedWriter(final String file) throws FileNotFoundException {
        return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
    }

    private static void copyContent(final BufferedReader reader, final BufferedWriter writer, final String marker) throws IOException {
        String line = reader.readLine();
        while((line != null) && !line.equalsIgnoreCase("/start" + marker)) {
            line = reader.readLine();
        }

        line = reader.readLine();
        while((line != null) && !line.startsWith("/start")) {
            writer.append(line);
            writer.newLine();
            line = reader.readLine();
        }
    }
}

Entspricht das deiner Vorstellung? Userinput müsste halt noch hinzugefügt werden.

Gruß

crusherd

EDIT: Mist, zu langsam...
 
AW: Java - Sektion aus großer Datei extrahieren

Die UserInput-Funktion ist bereits fertig. Ich probiere morgen mal beide Methoden aus und berichte, danke schon mal. :)
 
AW: Java - Sektion aus großer Datei extrahieren

Push - Ich versuche gerade für die Anwendung noch einen Zip-Import zu implementieren. Der Nutzer gibt den Namen der .zip, den Namen der .txt-Datei in dem .zip und den Namen der heraus zu kopierenden Sektion ein. Der Code für das Verarbeiten und den .zip-Import sieht momentan so aus:

Code:
        private static void zipImport(String zipSourceName, String zipContentName, String sectionName)
	{
		try (ZipFile zip = new ZipFile(zipSourceName))
		{
			ZipEntry content = new ZipEntry(zipContentName);

			if (!content.isDirectory())
			{
				InputStream input = zip.getInputStream(content);
				BufferedReader inputReader = new BufferedReader(new InputStreamReader(input));
				PrintWriter outputWriter = new PrintWriter(new FileWriter(sectionName + ".dmp"));

				verarbeiteDatei(inputReader, outputWriter, sectionName);
			}
		}
		catch (FileNotFoundException e)
		{
			System.out.println("'" + zipSourceName + "' not found.");
		}
		catch (NullPointerException e)
		{
			System.out.println("'" + zipContentName + "' not found.");
		}
	}

        private static void verarbeiteDatei(BufferedReader inputReader, PrintWriter outputWriter, String sectionName)
	{
		String line = "";
		String searchWord = "/start ";
		String sectionStart = searchWord + sectionName;
		try
		{
			while (line != null && !line.equals(sectionStart))
			{
				line = inputReader.readLine();
			}

			line = inputReader.readLine();

			while (line != null && !line.startsWith(searchWord))
			{
				outputWriter.println(line);
				line = inputReader.readLine();
			}
		}
	}

Dieser Code kann die .zip und die darin enthaltene Datei zwar lesen, allerdings fehlen teilweise einige Zeilen, je nach Position der Sektion. Heißt: Wenn in meiner Quell-Datei in der 1. Zeile /start baum steht und ich das nun aus der .txt in der .zip raushaben möchte, fehlen in der vom Programm erstellten Datei am Ende etwa 6 Zeilen der Sektion. Liegt /start baum in der Quell-Datei z.B. in Zeile 20000, dann fehlen am Ende gleich mehrere 100 Zeilen. Ich kann mir gerade bei bestem Willen nicht erklären, warum dem so ist. Jemand Vorschläge? :huh:
 
Zuletzt bearbeitet:
AW: Java - Sektion aus großer Datei extrahieren

Meine Vermutung ist, dass nicht alles aus dem Schreibpuffer des PrintWriters in die Ausgabedatei geschrieben wird, bevor sich das Programm beendet, da du ihn niemals schließt und auch keinen "Flush" durchführst.
 
AW: Java - Sektion aus großer Datei extrahieren

Also einfach zu dem try-Block der zipImport() noch einen finally packen und dann schließen? Probiere ich gleich mal aus, danke schon mal! :)
 
Hi,

Sorry für die etwas späte Antwort.
Mit einem flush() werden die restlichen Daten in die Datei geschrieben, die momentan im Puffer sind.
Ein close() schließt meines Wissens nach nur die Datei. Oder wird intern auch zuerst ein flush() durchgeführt?
In der Javadoc steht dazu leider nichts konkreteres.

Gruß
crusherd
 
AW: Java - Sektion aus großer Datei extrahieren

http://docs.oracle.com/javase/8/docs/api/java/io/PrintStream.html#close-- schrieb:
Closes the stream. This is done by flushing the stream and then closing the underlying output stream.

Ist doch ziemlich konkret.
 
AW: Java - Sektion aus großer Datei extrahieren

Verhält sich bei PrintStream aber auch nicht anders.

http://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html#close%28%29 schrieb:
Closes the stream. This is done by flushing the stream and then closing the underlying output stream.

Update:

Ist natürlich blödsinn. Hatte nicht darauf geachtet, dass du zwar von PrintWriter schreibst, aber die Dokumentation zu PrintStream verlinkt hast.
 
Verhält sich bei PrintStream aber auch nicht anders.

Update:

Ist natürlich blödsinn. Hatte nicht darauf geachtet, dass du zwar von PrintWriter schreibst, aber die Dokumentation zu PrintStream verlinkt hast.

Da hat mir die copy&paste funktion auf dem Smartphone dazwischen gefunkt. Die Seite mit Printwriter war offen, aber die Url hat immer noch auf PrintStream gezeigt. :-(

Gruß
crusherd
 
AW: Java - Sektion aus großer Datei extrahieren

Bei größeren Dateien würde sich NIO anbieten. Das ist gerade bei großen Datensätzen viel performanter.

Wäre mir neu. Kannst du das irgendwie durch Benchmarks belegen oder wenigstens logisch begründen?

Kann mich aber natürlich auch irren. Habe die letzten Jahre kaum etwas mit Java gemacht.
 
Zurück