Java In eine Datei in bestimmte Zeile schreiben

Riker

Lieutenant
Registriert
Jan. 2005
Beiträge
862
Hallo!

Ich stehe gerade vor folgendem Problem - wahrscheinlich habe ich gerade nen Brett vorm Kopf:
Ich habe eine Datei, z.B. "a.txt".
In dieser steht folgendes:

1;a;2;b;3;c;4;d;
5;e;6;f;7;g;8;h;
....

Nach meinem Schleifendurchlauf soll diese Datei wie folgt aussehen:

1;a;2;b;3;c;4;d;OK;
5;e;6;f;7;g;8;h;OK;
....


Nun greife ich mit einem BufferedWriter auf diese Datei zu und sage diesem auch, dass es diese Datei bereits gibt.
Aber wie kann ich dem BufferedWriter sagen:

Nehm die erste Zeile :: Schreib dahinter String,
Nehm die zweite Zeile :: Schreib dahinter String,
...

Evtl. hat hier ja jemand einen Denkanstoß für mich...

Danke!
 
Naja, ich würde die Datei zeilenweise in eine Liste einlesen...dort kannst Du dann einfach an jeden Listeneintrag (Zeile) den gewünschten String anhängen (concatenieren) und die ganze Liste wieder zeilenweise in die Zieldatei schreiben.

z.B so:

BufferedReader in = new BufferedReader(new FileReader("a.txt"));

String extraString = "OK";

//einlesen
ArrayList<String> content = new ArrayList<String>();
String str = "";
while((str=in.readLine()) != null)
content.add(str);

//verändern und schreiben
BufferedWriter out = new BufferedWriter(new FileWriter("a.txt"));
for(String line : content)
out.write(line+extraString+"\n");

out.close();

Wenn Du mit Reader/Writer arbeiten willst fällt mir da auf die schnelle gerade nichts intelligenteres ein...

Ansonsten könntest Du noch schauen ob Du mit den InputStream/OutputStream Klassen weiterkommst...dann wird es aber etwas aufwendiger...
 
Zuletzt bearbeitet:
An diese Variante habe ich auch schon gedacht.
Das Problem ist einfach, dass ich nicht weiß, was für eine Datei ich erwarte (also in diesem Fall jetzt schon, allerdings wird diese Methode auch auf andere Textdokumente angewendet die 500.000 Zeilen überschreiten könnten und da wird es schon laufzeitkritisch... :( ).
Ich schau mich mal im den Inout-/OutputStream Klassen um, evtl. finde ich da ja die Erleuchtung...

Für weitere Vorschläge/Offenbarungen bin ich gerne offen... :)

EDIT:
Ich danke Dir schon einmal, für den Vorschlag. Aber dies sollte - wie gesagt - nur die letzte Notlösung sein, da die Helperklasse in dem sich die Methode befindet, in Umgebungen läuft, wo es auf jede Sekunde ankommt... :/
 
Zuletzt bearbeitet:
Du kannst mit FileChannel an eine beliebige Position in einer Datei schreiben. Dann brauchst du sie nicht komplett neu zu schreiben. Allerdings muss man noch schauen wie man die Positionen aller Zeilenenden am effizientesten findet.

EDIT: Die Aussage war Blödsinn. Das funktioniert leider nicht, da einfach überschrieben wird.
 
Zuletzt bearbeitet:
Du könntest mit 2 Dateien arbeiten. Also

- aus Datei A zeilenweise lesen
- Zwischenergebnis in Variable speichern (= enthält die ganze Zeile)
- Variable manipulieren (= Anhängen des OK)
- Variable in Datei B schreiben

Am Ende:

- Datei A löschen
- Datei B in Datei A umbenennen

fertig.

Das hat den Vorteil, daß Du den Arbeitsspeicher nicht sprengst. Die Geschwindigkeit des Programms wird allerdings von der Geschwindigkeit der Festplatte abhängen. Die Anzahl der Prozesse, welche simultan auf das Laufwerk zugreifen, ist auch ein wichtiger Faktor.

Einfach mal ausprobieren.

PS: Ich kenne mich in Java nicht so aus. Daher eine Variante, die für alle Programmiersprachen geht.
 
DjNDB schrieb:
Allerdings muss man noch schauen wie man die Positionen aller Zeilenenden am effizientesten findet.

Man muss wahrscheinlich zeichenweise durch die gesamte Datei gehen, nach Zeilenumbrüchen suchen, und das OK jeweils davor schreiben.
Dabei gibt es zu beachten, dass es verschiedene Möglichkeiten gibt Zeilenumbrüche darzustellen. Entweder wird einfach definiert wie Zeilenumbrüche in den einzulesenden Dateien auszusehen haben, oder man muss es flexibel implementieren.
 
Hallo!

Danke nochmals, für die ganzen Vorschläge!
Ich bin nun mit der Klasse RandomAccessFile glücklich geworden.
Mit dieser schreibe ich nun das "OK;" zwar nicht an das Ende, aber an den Anfang jeder Zeile. Ist mir soweit eigtl. auch schnuppe, da ich es von dort eigtl. sogar noch besser auslesen kann... :)
Nun stellt sich mir aber eine Frage:
Ich habe jetzt zwei Reader, die gleichzeitig auf diese Datei schauen.
Zuerst den BufferedReader und dann noch den Reader der RandomAccessFile Klasse.
Gehen wir das Szenario durch:

while (Datei != null) {

1. BufferedReader liest die Datei ein.
2. BufferedReader liest die erste Zeile.
3. RandomAccessReader liest die erste Zeile.
4. RandomAccessReader schreibt das "OK;" vor die erste Zeile.

}

Ich sehe das Problem evtl. bei 1.!
Könnte es sein, dass der BufferedReader sich die Bytezahl der Datei beim Einlesen merkt (Bsp: 1024 Bytes), der RandomAccessReader dann jedoch vor jede Zeile das "OK;" davor schreibt und das Skript dann evtl. nicht bis zum Ende der Datei kommt (da sich im Laufe dieses Prozesses ja auch die Bytezahl vergrößert --> Bei Schritt 1 ist die Datei 1024 Bytes groß, aber nach bereits 5 Zeilen wäre sie z.B. 1126 Bytes groß, der BufferedReader würde aber nur bis zum 1024. Byte lesen!?)?.
Oder liege ich hier nun falsch?

Danke!
 
Wozu benötigst Du 1. und 2. ?
Kann die RandomAccessFile Klasse nicht beides übernehmen, also Zeile einlesen und danach dann das OK davor schreiben ?

Wenn Du mit 2 Readern auf die selbe Datei zugreift könnte das schon Probleme geben...habe das selbst immer vermieden daher weiss ich nicht genau was dann passiert ;)
 
Etw. kompliziert - ein anderer Entwickler hatte ebenfalls schon eine Helperklasse geschrieben, die mit dem BufferedReader auf einzelne Zeilen und Spalten auf solche Daten zugreifen kann. Meine Helperklasse habe ich nun noch sozusagen dazwischen geschaltet... :)
Aber ich bin gerade am testen, was nun passiert. Hab einfach mal ne 200 Mb große Datei erzeugt und lasse das Skript nun darauf laufen und schaue, ob das Skript bis zum Ende der Datei durchrennt...

Wenns erfolgreich ist, poste ich den Codeschnipsel hier für andere rein. Ist wirklich nicht viel, sowas zu implementieren, aber finden tut man es nirgends... :)
 
Riker schrieb:
Aber ich bin gerade am testen, was nun passiert. Hab einfach mal ne 200 Mb große Datei erzeugt und lasse das Skript nun darauf laufen und schaue, ob das Skript bis zum Ende der Datei durchrennt...

Darauf würde ich mich aber nicht verlassen. Besser ist es in der Doku nachzulesen was genau passiert.
Noch besser wäre es das ganze voneinander zu trennen. Gibt es irgendeinen Grund weshalb das Schreiben gleichzeitig mit dem bisherigen Lesen stattfinden muss?
Ergänzung ()

Ich habe jetzt nichts gefunden das eine eindeutige Aussage darüber zulässt ob ein Problem auftritt oder nicht. Da kann man nur durch Nachdenken rangehen.
Der Knackpunkt liegt denke ich im BufferedReader. Zwar wird auf Dateien über Streams zugegriffen, sodass aktuelle Änderungen reflektiert werden, aber sobald du Pufferung (=>BufferedReader) verwendest besteht natürlich die Möglichkeit, dass der Bereich der Datei schon eingelesen wurde, den du später verändern möchtest. Beim nächsten Lesen aus dem BufferedReader wären dann die Daten inkonsistent.
Der BufferedReader bekommt ja nur einen Reader übergeben, und weiß daher nichts davon dass sich die Datei geändert hat und er den Puffer neu lesen müsste.
 
Japp - ich hab es nun getestet und es sieht dann wie folgt aus:

Aus:

1;a;2;b;3;c;4;d;
5;e;6;f;7;g;8;h;
....

Wollte ich nun haben:

OK;1;a;2;b;3;c;4;d;
OK;5;e;6;f;7;g;8;h;
....

Aber ich bekomme:

OK;;2;b;3;c;4;d;
OK;;6;f;7;g;8;h;
....

Also schneidet der mir die ersten drei Zeichen weg, die nun vom "OK;" eingenommen werden :/
 
Könntest Du das Problem nicht dadurch umgehen, daß Du die Datei von hinten nach vorne durchsuchst? Dann sollte es doch egal sein, ob sich die Dateigröße ändert.
 
Aktuelles Problem dargestellt (wie ich es mir denke):

ich habe eine Methode next().
Diese Methode brauche ich, um mir die Zeilen einzeln auszulesen (z.B. für eine while-Schleife :: solange was zurückkommt, mach dies und das...).
Diese Methode nutzt raf.readLine(); (raf = Objekt von Klasse RandomAccessFile).

Die Methode checkLine() greift aber auch auf die Zeilen, die ich ändern möchte über ein raf.readLine(); und schreibt in diese Zeile dann das "OK;".

Dateninkonsistenz:

checkLine(); ruft Zeile auf, merkt sich "diese Zeile ist 512 bytes groß, die nächste beginnt bei bytenummer 513" und schreibt "OK;" davor. Aber er weiß ja auch, dass die nächste eben bei 513 beginnt, also vergrößert dieser diese Zeile nicht, sondern überschreibt die Daten, damit er die
nächste Zeile wieder aufrufen kann...

Danke für deinen Vorschlag - aber ich glaube ich brauche etwas, wo ich dem raf sagen kann "schreibe "OK;" davor und vergrößer die Zeile um "OK;"'s Bytes... :)
 
Langsam wird es kompliziert nachzuvollziehen was du machst. Eigentlich wolltest du doch anfangs nur in eine Datei schreiben.

Lad am besten mal Quellcode hoch.
 
Hier schnippel vom Quellcode mit nen wenig Kommentar:


Klasse "Korrektur.java" - hier wird auf die Methoden zugegriffen. StringResultSet ist dabei die Helperklasse.
Code:
StringResultSet rs = new StringResultSet(
					INPUTFILENAME, true); /**INPUTFILENAME = Der Dateiname der txt - Datei; true = Kopfzeilen/Überschrift vorhanden **/
			
			while (rs.next()) {
				if (!rs.getString(1).equals("OK;")) {
				rs.checkLine();
/** Solange aus der Datei etwas zurückkomt und solange die erste Spalte != "OK;" ist, führe die Methode checkLine(); aus **/


Hier die Methode aus der Helperklasse.
Code:
public void checkLine() throws IOException {
		aktuelleZeile = raf.readLine();
		if (aktuelleZeile != null) {
			String ok = "OK;";
			
			raf.writeBytes(ok);
			
		}
		else {
			throw new IOException();
		}
	}


Was daraufhin passiert könnt ihr ja im obigen Bsp. sehen.
Dort steht, wie die Datei aussieht, was bei rumkommt und was ich haben möchte.

Grüße!
 
Riker schrieb:
Hier schnippel vom Quellcode mit nen wenig Kommentar:

Das ist mir zu unvollständig um die komplette Interaktion nachzuvollziehen.

Mach am besten mal ein komplettes Minimalbeispiel, das sich auch ausführen lässt.
 
Mehr kann ich leider nicht preisgeben... :(
Bin Consultant und mache hier was für den Kunden. Wenn ich hier mehr preisgebe, dann wird der Code zu offensichtlich und ich würde wohl nen wenig Ärger mit dem Kunden bekommen... hmpf - morgen ist Entwickler-Telko... Evtl. hat ja einer meiner Kollegen ne glänzende Idee :)
Sollte dem so sein, werde ich diese hier posten.

Grüße!
 
@ModellbahnerTT
Das habe ich leider auch schon probiert :)
Dann matscht der mir das ganze nach Lust und Laune zusammen.
Ich muss ihm irgendwie sagen können:

while (Zeile != null) {

1. Lies die Zeile (Bsp.: 512 Bytes)
2. Die Zeile wird von nun an nicht nur 512 Bytes, sondern 530 Bytes Groß sein (Zeile aus 1. + das "OK;")
3. Schreib das "OK;" hinter die Zeile

}
 
Das Problem ist, dass es entgegen meiner vorherigen Aussage in Java wohl doch nicht möglich ist in eine Datei Daten an beliebiger Stelle einzufügen. Es wird ab dieser Position überschrieben.

Du kannst dem Vorschlag von Ruheliebhaber folgen, und eine Kopie der Datei erstellen. Dann musst du allerdings alle Daten einmal lesen und schreiben.

Für alles weitere müsste man wissen was das OK aussagt, und ob es in der selben Datei stehen muss.
Sonst könnte man es auslagern, wodurch man dann nicht alle Daten nochmal schreiben müsste.
 
Zurück
Oben