TXT-Datei blockweise auslesen

Hallo,

ich muss eine 2.30 GB große Textdatei einlesen und in einem anderen Dokument wieder einschreiben.
Ziel der Sache ist es, zwei Dateien, die von einem anderen System ausgegeben werden, zu einer zusammenzufassen. Ich unterhielt mich gestern bereits schon im java forum darüber, jedoch ist das nun geschlossen und ich wurde quasi über News zu euch weitergeleitet. Der letzte Stand war, dass ich mit der File Klasse arbeiten solle, jedoch scheint das nicht ordentlich zu klappen.

Ich erhielt einen Link, wo ich einen Beispiel Code zur Umsetzung finden sollte, doch ich glaube das war nicht so erfolgreich…
http://openbook.galileocomputing.de/java7/1507_05_003.html#dodtp27aa8d91-3ce4-47dd-a277-5538dbaaa2a6

Ist dieser Code richtig?


package com.tutego.insel.nio2;

import java.io.IOException;	
import java.net.*;
import java.nio.charset.Charset;
import java.nio.file.*;
	
public class Main {



	 public static void main( String[] args ) throws IOException, URISyntaxException
	  {
	    URI uri = ListAllLines.class.getResource( "/lyrics.txt" ).toURI();
	    Path path = Paths.get( uri );
	    System.out.printf( "Datei '%s' mit Länge %d Byte(s) hat folgendes Zeilen:%n",
	                       path.getFileName(), Files.size( path ) );
	    int lineCnt = 1;
	    for ( String line : Files.readAllLines( path, StandardCharsets.UTF_8 ) )
	      System.out.println( lineCnt++ + ": " + line );
	  }


Ich bin ein kompletter Anfänger in Java. Ich weiß dass Einiges importiert werden kann, wie z.B. der BufferedReader, jedoch kann ich bei diesem Code nicht wirklich etwas importieren, nur jeweilig Klassen erstellen, doch diese sind dann ohne Inhalt - macht nach meinem Verständnis also wenig Sinn… was kann ich dagegen tun?

Hoffe ihr könnt mir helfen und bedanke mich schonmal im Voraus :slight_smile:

Mit freundlichen Grüßen

cyan

Also prinzipiell kann man Dateien folgendermaßen einlesen:

BufferedReader br = new BufferedReader(new FileReader(inputfile));
String line = "";
while((line = br.readLine()) != null) {
   //Hier kann jetzt Zeile für Zeile ausgelesen werden.
}
br.close();```

Und so Daten in eine Datei schreiben:

```File outputfile = new File(path);
FileWriter fw = new FileWriter(outputfile);
fw.write("Hier der Text der in die Datei geschrieben wird");
fw.flush(); //Bewirkt, dass die Daten geschrieben werden.
fw.close(); //Schließt den Stream.```

ExceptionHandling muss natürlich auch gemacht werden.

Importiert werden müssen folgende Klassen, falls Du keine IDE benutzt:
```import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;```

Hallo,

ja mithilfe des zeilenweisen Einlesen habe ich es bereits versucht, jedoch dauert 2,30 GB viel zu lange. Mir fiel gerade auf, dass ich das neuste JDK nicht benutze und mir somit java 7 nicht zur Verfügung steht sowie die nio2 nicht. ich probiere es damit mal

[QUOTE=cyan]ich muss eine 2.30 GB große Textdatei einlesen und in einem anderen Dokument wieder einschreiben.
Ziel der Sache ist es, zwei Dateien, die von einem anderen System ausgegeben werden, zu einer zusammenzufassen. [/QUOTE]

Die Basis würde nach meinem Verständnis so aussehen:
Du hast eine 2.3GB große Textdatei und eine andere Datei von einem anderem System.
Jetzt willst du beide Dateien in einer Datei “verbinden” (hintereinander schreiben?).

Mit dem Ansatz von MiDniGG solltest du da relativ weit kommen:
Mach dir einen Ausgabestream auf die Zieldatei auf.
Jetzt ließt du die erste Quelldatei Zeilenweise ein und schreibst jede gelesene Zeile direkt auf den Ausgabestream.
Ist die erste Quelldatei durch, lässt du den Ausgabestream offen und machst das gleiche mit der zweiten Quelldatei (Zeilenweise schreiben).
Am Ende kann dann der Ausgabestream geschlossen werden und du hast eine große Datei, die den Inhalt von beiden Quelldateien enthält.

Gruß

Hallo,

also die zwei Dateien entstammen vom Mainframe und werden beide als Textdokument ausgegeben. Der Inhalt beider Dateien soll hintereinander, also Inhalt von Datei 1 zu erst und danach Inhalt von Datei 2 hinten dran, geschrieben werden.

Ich habe den Vorschlag mal übernommen, jedoch verstehe ich nicht ganz, wieso “java.io.InpuStreamReader”, “inputfile” und “outputfile” als ungenutzt betitelt werden.

und zusätzlich noch die Frage, “fw.write” schreibt einen Text hinein, aber muss da nicht z.B. die Variable “line” stehen, um die Eingelesenen Zeilen zu schreiben?

Bei diesem Beispiel habe ich nun “in.txt” (2.3 GB groß) in den Projektordner verschoben. Ebenso die “out.txt” (0 KB groß). Er sollte doch nun die Zeilen aus der “in.txt” lesen und sie in “out.txt” schreiben oder?

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;

public class  Main {

	public static void main(String[] args) {
	
		try {	
				File inputfile = new File("C:/Users/harded/workspace/Merge");
				BufferedReader br = new BufferedReader(new FileReader("in.txt"));
				String line = "";
				while((line = br.readLine()) != null) {
				   //Hier kann jetzt Zeile für Zeile ausgelesen werden.
			}
				br.close();
		} catch (IOException e){
			System.out.println("Fehler" + e);
		}
	
	
		try {
				File outputfile = new File("C:/Users/harded/workspace/Merge");
				FileWriter fw = new FileWriter("out.txt");
				fw.write("Hier der Text der in die Datei geschrieben wird");
				fw.flush(); //Bewirkt, dass die Daten geschrieben werden.
				fw.close(); //Schließt den Stream.
		} catch (IOException e){
			System.out.println("Fehler" + e);
		}		
				
				
	}
	
}

Gruß cyan

Es sollen einfach die Inhalte der beiden Dateien in eine kopiert werden?
Das heißt Du musst die Inhalte nicht interpretieren, auswerten, umwandeln?
Dann verwende statt dem Reader und dem Writer einfach Streams, das Zeilenweise einlesen und Schreiben kann man sich damit sparen. Einfach ähnlich wie beim Reader per InputStream und Schleife in ein byte Array (damit sind ohne Problem mehrere MB auf einmal möglich - habs noch nicht ausprobiert aber auf entsprechender Hardware und eingstellter Heap Size der JVM sogar GB? ) einlesen und per OutputStream wieder rausschreiben.

Uff, ich bin eher ein Einsteiger was Java angeht. Das sagt mir gerade gar nichts…

Aber ja, es bedarf nur das reine Kopieren. Ich habe auch schon bereits einen zeilenweisen Einleser geschrieben mit dem Ergebnis dass es zu lange dauert, nur dachte ich dass der Vorschlag hier eventuell etwas schneller laufen könnte. Mein Chef ist für jede schnellere Lösung dankbar :smiley:

Naja, ist doch offensichtlich. Du verwendest die Variablen an keiner Stelle in Deinem Code. Du definierst nur in.txt und out.txt die dann relativ zum Ausführungverzeichnis gesucht werden. Die zuvor in den File Objekten definierten Verzeichnisse sind irrelevant und eben ungenutzt.

So in etwa geht das ganze mit Strings, falls du diese Strings tatsächlich weiter verarbeiten müsstest.

 public static void main(String[] args) {
   
        try {  
                File inputfile = new File("C:/Users/harded/workspace/Merge/in.txt");
                BufferedReader br = new BufferedReader(new FileReader(inputfile));
                String line = "";
                File outputfile = new File("C:/Users/harded/workspace/Merge/out.txt");
                FileWriter fw = new FileWriter(outputfile);

                while((line = br.readLine()) != null) {
                    fw.write(line);
            }
                br.close();
                fw.flush(); //Bewirkt, dass die Daten geschrieben werden.
                fw.close(); //Schließt den Stream.
        } catch (IOException e){
            System.out.println("Fehler" + e);
        }
   
   
        
               
               
    }

Wenn du nur eine Datei kopieren möchtest warum lässt du dass nicht einfach dein Betriebssystem machen?
in dem du einfach mtit Runtime.exec den copy-befehl richtig aufrufst?

String[] args = { "CMD", "/C", "COPY", "/Y", src_file_path, dest_file_path};
Process p = Runtime.getRuntime().exec(args);
p.waitFor();

Mein Fehler, ist ein Überbleibsel von meinem Code. Der Import vom InputStreamReader kann raus. Den hatte ich drin, um den Pfad aus der Kommandozeile auszulesen.
input- und outputfile werden nicht verwendet, weil Du sie nicht verwendest… :smiley: Sollte so sein:

BufferedReader br = new BufferedReader(new FileReader(inputfile));```

[QUOTE=cyan;20361]und zusätzlich noch die Frage, "fw.write" schreibt einen Text hinein, aber muss da nicht z.B. die Variable "line" stehen, um die Eingelesenen Zeilen zu schreiben?[/QUOTE]
Natürlich muss es das. Aber alles muss ich ja auch nicht vorkauen. :-P

[QUOTE=cyan;20361]Bei diesem Beispiel habe ich nun "in.txt" (2.3 GB groß) in den Projektordner verschoben. Ebenso die "out.txt" (0 KB groß). Er sollte doch nun die Zeilen aus der "in.txt" lesen und sie in "out.txt" schreiben oder?[/QUOTE]
Ja.

Nach kurzer Suche im Netz bin ich sogar auf ein fertiges Bsp gestossen.
http://openbook.galileodesign.de/javainsel5/javainsel12_003.htm#Rxx747java12003040003FA1F0151F3

Nunja ich nutzte das obige Beispiel von MiDniGG und passte es nach meinem Ermessen mit meinen Daten an…

Anscheinend ist es eher nicht zu gebrauchen was ich dort eintrug.

File inputfile = new File(path);
BufferedReader br = new BufferedReader(new FileReader(inputfile));

Es war doch nicht so gedacht, path und inputfile so zu übernehmen oder etwa doch?

[QUOTE=cyan]Nunja ich nutzte das obige Beispiel von MiDniGG und passte es nach meinem Ermessen mit meinen Daten an…

Anscheinend ist es eher nicht zu gebrauchen was ich dort eintrug.

File inputfile = new File(path);
BufferedReader br = new BufferedReader(new FileReader(inputfile));

Es war doch nicht so gedacht, path und inputfile so zu übernehmen oder etwa doch?[/QUOTE]

Teilweise. :slight_smile:
Der FileReader erwartet ein File-Objekt. Also muss da inputfile rein. Und File erwartet einen String also C:/temp/file.txt bspw.

Habe die Veränderungen nun daran vorgenommen.

jedoch verlangt er von mir, sofern ich den write-part in seinem eigenen try-catch Block lasse, dass ich die lokale Variable “line” erstelle. Kopiere ich den Inhalt allerdings in den try-catch Block vom read-part, kommt kein Fehler. Nach Ausführung sagt er allerdings, dass er die Quelle nicht kennt und verweist auf fw.write(line);

Mal einen Blick in den verlinkten Beispielcode geworfen? Eine kleine Änderung und Du kannst damit effizient beliebig viele Dateiinhalte in eine Datei kopieren.

…und ansonsten ist die Reihenfolge von AmunRa die richtig:
http://forum.byte-welt.net/showpost.php?p=20388&postcount=9

Ja habe mir den Code auf galileodesign angeschaut unter 12.4.8. bin etwas überfordert muss ich sagen und versuche noch ihn zu verstehen.

Etwas verwirrend ist vielleicht die Aufteilung in mehrere Methoden und das verschachtelte try-catch-finally Konstrukt.
Auf das wesentliche reduziert lässt sich der Code aus dem Beispiel in etwa so zusammenfassen

    FileInputStream in = new FileInputStream(new File("in.txt"));
    FileOutputStream out = new FileOutputStream(new File("out.txt"));

    byte[] b = new byte[0xFFFFFF];
    int len = 0;
    
    while ((len = in.read(b)) != -1) {
        out.write(b, 0, len);
    }
    out.close();
    in.close();
} catch (Exception e) {
    e.printStackTrace();
}```

ok ich probiere das gleich mal aus.

Mittlerweile habe ich den Code von MiDniGG zum Laufen bekommen und er kopiert auch alles in einer Überragenden Geschwindigkeit, jedoch macht er nach einer Zeile keinen Zeilenumbruch, sondern zieht gnadenlos durch und schreibt es in eine Zeile.

Wenn ich weiß wie lang eine Zeile im Original ist, ist es dann möglich einen Zeilenumbruch automatisiert zu setzen?

Wenn du nur eine 1:1 Kopie der Datei machen willst, dann verwendest du besser den Code von _Michael. Dann werden auch die Zeilenumbrüche 1:1 kopiert (und seine Methode sollte nochmal um einiges schneller sein).