Hallo,
Ich versuche gerade mit Java eine bestimmte Zeile in der Datei zu ändern. Uns war möchte ich nach der Zeile eine “ID” anhängen. Mein Programm funktioniert nur noch nicht ganz richtig. Entweder überschreibt es die Datei und fügt nur die Zeile ein oder es fügt die Zeile nochmals zum Ende der Datei ein, sodass sie doppelt vorhanden ist. Es soll aber die Position der Zeile in der Text- bzw. CFG-Datei möglichst nicht verändern, sondern wenn in der Zeile der String “S:LightItems” vorhanden ist, soll er am ende der Zeile etwas anhängen.
Hier mal mein Java-Code und die cfg-Datei.
try {
BufferedReader in = new BufferedReader(new FileReader("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer.cfg"));
String line = null;
while ((line = in.readLine()) != null) {
if (line.contains("S:LightItems"))
{
if (!line.contains("4020"))
{
BufferedWriter out = new BufferedWriter(new FileWriter("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer.cfg"));
String id = ",4020=10";
out.write(line+id);
out.close();
}
}
}
in.close();
}
catch (IOException e) {
e.printStackTrace();
}
}```
Configuration file
####################
general
####################
general {
# Item IDs that shine light while held. Armor Items also work when worn. [ONLY ON YOURSELF]
S:LightItems=50,89=12,348=10,91,327,76=10,331=10,314=14
# Item IDs that do not shine light when held in water, have to be present in LightItems.
S:TurnedOffByWaterItems=50,327
man kann files nicht ändern, sondern lediglich komplett in den RAM lesen, dann damit was machen, und verändert wieder rausschreiben
und DAS macht dein programm ja mal nicht
und nein, code gibts nicht, denn mein erster satz erklärt schon was zu tun ist, die implementierung nimmst du bitte selbst vor
Komplett in den RAM lesen kann man sich sparen wenn man die Datei direkt wieder schreibt.
Eine Möglichkeit dazu:
1- InputStream zu Datei A aufmachen
2. OutputStream zu Datei A-tmp aufmachen (leere Datei)
3. InputStream Zeile für Zeile durchgehen und direkt wieder in A-tmp speichern
4. Wenn die entsprechende Zeile gefunden wird ändern und in A-tmp speichern
5. Mit den restlichen Zeilen weitermachen
6. Streams schließen, A löschen und A-tmp in A umbenennen
Man muss jede Zeile lesen und schreiben. Dazu musst Du allerdings entweder die Datei komplett einlesen (z.B. in eine List) und anschließend neu rausschreiben oder mit einer temporären Datei arbeiten um direkt beim Lesen den (evtl. neuen) Inhalt in eine zweite Datei schreiben zu können.
Letztere Variante schon (vor allem bei großen Dateien) den Speicherverbrauch.
Da es sich um eine Konfiguration handelt empfehle ich dir vllt lieber ein xml-File zu verwenden. Es hat die Vorteile, dass es sehr übersichtlich zu strukturieren ist, leicht veränderbar ist, und leicht zu lesen ist.
Properties in Java bieten ähnliche Funktionen. Es schadet aber nicht, sich selber mal Gedanken über so etwas zu machen, dann bekommt man auch gleich etwas Übung im Dateihandling @MZ80A
Ich denke mal, es war eher gemeint, dass man Dateien nicht einfach aufpusten kann um etwas in die Mitte zu schieben (Der Rest von der Datei auf der Festplatte wandert ja nicht „magisch“ weiter). Anhängen geht in der Regel ohne Probleme.
Natürlich gibt es auch für solche Fälle Abstraktionen, wie z.B. RandomAccessFile.
Gruß
Edit: Ich sehe gerade der gleiche Thread ist auch im JF offen
Ich weiß gerade einfach nicht wie ich anfangen soll. Wie öffne ich denn den InputStream für Datei A und gehe dann Zeile für Zeile vor? Ich weiß, dass ich eine while Schleife verwenden muss, aber wie öffne ich den InputStream.
So wie in deinem aktuellen Code auch BufferedReader in = new BufferedReader(new FileReader("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer.cfg"));
Eine Schleife die über die Zeilen geht hast du ja auch schon.
Edit: FileReader kapselt dir im Prinzip einen InputStream auf die Datei (für Dateien die aus Zeichen bestehen).
Die Alternative wäre ein FileInputStream mit einem InputStreamReader (hier aber nicht relevant).
Die hart codierten Aufrufe der Laufwerke und Verzeichnisses mit maskierten Backslashes auch nicht unbedingt “state of the art”. Das Programm wird damit plattformabhängig, ja sogar systemabhängig…
Habe es mittlerweile hinbekommen. Bräuchte aber eure Hilfe bei der Optimierung.
Gibt es eine Möglichkeit die while Schleife oder Funktion komplett abzubrechen wenn die ID “4020” vorhanden ist?
Die Formatierung in der jetzigen Datei sieht etwas anders aus als der Vorgänger. Kann man hier noch einfach Zeilenumbrüche einfügen?
public void run() {
try {
BufferedReader in = new BufferedReader(new FileReader("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer.cfg"));
BufferedWriter out = new BufferedWriter(new FileWriter("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer_tmp.cfg"));
File f1 = new File("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer_tmp.cfg");
File f2 = new File("C:\\Users\\abt\\Desktop\\forge804\\mcp\\jars\\config\\DynamicLights_thePlayer.cfg");
String line = null;
while ((line = in.readLine()) != null) {
if (!line.contains("S:LightItems"))
{
out.write(line);
}
else {
if (!line.contains("4020"))
{
String id = ",4020=10";
out.write(line+id);
}
else {
out.write(line);
}
}
}
out.close();
in.close();
f2.delete();
f1.renameTo(f2);
}
catch (IOException e) {
e.printStackTrace();
}
}
# Configuration file##################### general####################general { # Item IDs that shine light while held. Armor Items also work when worn. [ONLY ON YOURSELF] S:LightItems=50,89=12,348=10,91,327,76=10,331=10,314=14,4020=10 # Item IDs that do not shine light when held in water, have to be present in LightItems. S:TurnedOffByWaterItems=50,327}
Warum erzeugst Du nicht zuerst die File Objekte und verwendest diese dann in den Reader/Writer?
zu 1. Warum willst Du abbrechen? Willst Du nicht die komplette Konfiguration wieder rausschreiben?
zu 2. Verwende einen Writer der Zeilenumbrüche “beherrscht”, z.B. PrintWriter
Wenn ich das richtig sehe, könnte man die Überprüfung könnte man in ein if else Konstrukt zusammenfassen.
Das stimmt. Kann man es nicht so ändern, dass es gar nichts neues schreibt wenn die ID vorhanden ist, oder geht das so schon? Auch wegen den Resourcen.
Prinzipiell könntest Du zuerst nur eine Schleife über die Datei laufen lassen, die überprüft, ob der Wert bereits verhanden ist und ggfs abbricht - ist aber nicht unbedingt notwendig.
Zu überlegen wäre die ursprüngliche Datei als Backup zu behalten - falls das nicht ohnehin schon passiert.
Habe es jetzt so belassen, funktioniert hervorragend. Mein einziges Problem ist nur noch der Pfad. Ich möchte es nämlich universell nutzen und dachte da an \config\bla.cfg einzufügen. So findet er den Pfad aber nicht.