String einer Textdatei in einzelne Stringobjekte pro Zeile aufteilen

Wuast

Bekanntes Mitglied
Hallo zusammen,

ich habe eine ArrayList mit Personenobjekten in einen String umgewandelt, um diesen in eine Textdatei zu speichern (überschrieben mittels bufferedwriter).
Code:
ListIterator <Personen> pIterator = ... // Methodenaufruf, passt
        while (pIterator.hasNext()) {
            for (Personen personen : personenKlasse.getPListe()) {
                String personendaten += personen.toString()+ "\n";
            }
            return personendaten;
        }

Das ganze soll auch wieder rückwärts funktionieren, um die ArrayList nach erneutem Programmstart wieder aufrufen und weiter bearbeiten zu können.

Mittels
Java:
try {
    String dateieInhalt = new String(Files.readString(p));    //Paths p = ... --> passt
        System.out.println(dateiInhalt);    //funktioniert einwandfrei (nur zur Kontrolle)
    } catch (IOException e) {
        e.printStackTrace();
    }
kann ich den String dann wieder einlesen. Das klappt, in der Konsole bekomme ich den String, sodass jedes Personenobjekt (auch mit entsprechenden Zeilenumbrüchen nach jedem Objekt) als String wiedergegeben wird. Aber es ist ja zu diesem Zeitpunkt noch "nur" ein großer String.

Jetzt möchte ich ja gerne die einzelnen, ursprünglichen Personenobjekte wieder zurück in die Ausgangsform bringen.

Dazu meine Fragen:
1) Ist es der "richtige" Weg, zunächst jede Zeile des Strings auszulesen und diese dann in eine String-Liste zu schreiben, damit jeder Index dann wieder zurück in die jeweiligen einzelnen Parameter geparst werden kann? Ich freunde mich gerade etwas mit den Streams an und dachte, das wäre dann vielleicht eine elegante Möglichkeit, zumal die Werte ohnehin dann noch nach einem bestimmten Parameter sortiert werden können sollen.
2) Wie geht das? 😀 (halbwegs elegant, ggf. mit einer dafür vorgesehenen Klasse) Hier verstehe ich zunächst also erstmal nicht, wie ich coden soll, dass jeweils immer nur eine Zeile auszulesen ist. Die Liste mit add.(ausgelesene Zeile) pro Zeile mithilfe einer Schleife zu füllen kriege ich dann hin denke ich.

Kann mir jemand weiterhelfen bzw. einen Tipp geben?
Code:
RandeomAccessFile file = new RandomAccesFile(p);     //p = path
file.readUTF();
Bietet sich das an?
 
Zuletzt bearbeitet:
Um mir mal selbst zu antworten, ich habe es über eine Schleife gelöst.
Java:
    public void textinhalteEinlesen() {
    
        try {
            pListe = (ArrayList<String>) Files.readAllLines(p);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        if (!pListe.isEmpty()){
            for (int i = 0; i < pListe.size(); i++) {
                System.out.println(pListe.get(i));        //gibt alle Indezes und somit die Liste aus 
                
                //jetzt würde ich versuchen den String zu splitten, dann zu parsen
                int alter;
                String name;
                double kontostand;
                
                // und dann mit den Werten ein neues Personenobjekt erstellen und dieses einer anderen ArrayListe / oder der alten (Upcycling hehe) hinzufuegen

        }
    }

Denke, das sollte gehen, oder hat jemand Einwände?
Nur geht das nicht auch eleganter mithilfe bestimmter Klassen oder Methoden, die ich vielleicht nicht kenne?
 
Noch ein Hinweis: Da die Klasse ArrayList bereits Serializable implementiert, könntest du direkt die ganze Liste mit einem Befehl in eine Datei speichern. Die in der Liste enthaltenen Elemente müssen allerdings ebenfalls Serializable implementieren.
 
Was mir aufgefallen ist: Im ersten Code hast du ein new String(...) Aufruf auf einer Methode, die bereits einen String zurück gibt. Das ist so unnötig.

Und bezüglich Serialization: Das ist zwar im Java Framework so mit drin, aber wenn es um das Laden/Speichern geht, würde ich immer dazu raten, auf eben diese Serialization zu vermeiden. Das kann zu schnell zu Problemen führen (Wenn sich Klassen z.B. ändern). Daher wäre es eher angebracht, sich da die XML oder JSON Serialisierung anzusehen mit z.B. Jackson oder gson. https://www.baeldung.com/jackson-object-mapper-tutorial
 
Und bezüglich Serialization: Das ist zwar im Java Framework so mit drin, aber wenn es um das Laden/Speichern geht, würde ich immer dazu raten, auf eben diese Serialization zu vermeiden. Das kann zu schnell zu Problemen führen (Wenn sich Klassen z.B. ändern). Daher wäre es eher angebracht, sich da die XML oder JSON Serialisierung anzusehen mit z.B. Jackson oder gson. https://www.baeldung.com/jackson-object-mapper-tutorial
Das würde auch für seine "selbstgestrickte" Lösung zutreffen.

Ansonsten hast du natürlich Recht und ich würde direkt zu einer DB, wie H2 greifen. Die Frage ist halt immer, wie viel Komplexität möchte ein Anfänger haben. Es ist am Anfang ein gutes Gefühl, wenn man es mal geschafft hat, Daten (in welcher Form auch immer) zu speichern und wieder zu laden. Der Hinweis auf die Problematik ist allerdings korrekt und wenn er es mit Serialisierung löst, dann sollte er das auch wissen und auch, dass es bessere Lösungen gibt. Die benötigen halt auch erneute Zeit sich damit zu beschäftigen und ich denke es ist gut, sich langsam "hoch zu arbeiten". Vielleicht auch dadurch, dass er seinen Ansatz weiter verfolgt und die Daten als Textdatei speichert. Dümmer wird man sicher nicht dadurch.
 
Vielleicht möchtest du auch einfach mal verschiedene Lösungen ausprobieren? Dann abstrahiere doch dein Verfahren in einem Interface.

Hier ein simpler Versuch.

Die Datenklasse
Java:
public class Person implements Serializable {

    private final static long serialVersionUID = 1L;

    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Das Interface
Java:
public interface PersonFile {

    void save(List<Person> personList);
    List<Person> load();
}

Anschließend kannst du dir konkrete Implementationen für verschiedene Dateiformate erstellen, z. B. TextPersonFile, SerializedPersonFile, JsonPersonFile, usw.

Ein konkrete Implementation des Interfaces
Java:
public class SerializedPersonFile implements PersonFile {

    private String filename;

    public SerializedPersonFile(String filename) {
        this.filename = filename;
    }

    @Override
    public void save(List<Person> personList) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
            oos.writeObject(personList);
            oos.flush();
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    @Override
    public List<Person> load() {
        List<Person> result = null;
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myfile"))) {
            result = (List<Person>) ois.readObject();
        } catch(ClassNotFoundException | IOException e){
            e.printStackTrace();
        }
        return result;
    }
}

Oder als CSV (nicht vollständig!)
Java:
public class CsvPersonFile implements PersonFile{

    private String filename;

    public CsvPersonFile(String filename) {
        this.filename = filename;
    }

    @Override
    public void save(List<Person> personList) {
        try (PrintWriter pw = new PrintWriter(new File(filename))) {
            for (Person person : personList) {
                pw.println(person.getName() + "," + person.getAge());
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    @Override
    public List<Person> load() {

        // todo implement load method

        return null;
    }
}

Die Anwendung
Java:
public class PersonFileSaverTest {

    public static void main(String[] args) {

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Hans", 42));
        personList.add(new Person("Felix", 33));

        // an dieser Stelle legst du fest, welches konkrete Format (= welche Implementation des Intefaces) verwendet wird
        PersonFile pfs = new SerializedPersonFile("myfile");
    
        // im weiteren Programm werden nur die Methoden des Interfaces verwendet
        // welches Verfahren dahinter steckt ist dem Programm an dieser Stelle egal
        // d. h. du kannst die Implementierung oben austauschen und dein Programm funktioniert weiterhin wie vorher
       
        // irgendwo in deinem Programm...
        pfs.save(personList);

        System.out.println(pfs.load());

    }
}
 
Zuletzt bearbeitet:
Merci für die Rückmeldungen!
Wow okay, das ist super interessant und etwas mehr zum Lernen als gehofft.
Muss ich mir mal auf meine "noch zu lernen"-Liste schreiben, gleich hinter Enums.
Falls es nur um Speichern/Laden und nicht um die Lesbarkeit (durch Menschen) der Datei geht evtl. Serialisierung?
Ohne, dass ich jetzt da richtig eingestiegen bin in die Serialisation (sondern nur anhand deiner ausführlichen Antwort - Danke dafür!! - und den geteilten Links hier): Bedeutet dies, dass ich - wenn ich die Daten vom String nicht einfach weiter in den Tiefen eines Programms verarbeiten will, sondern irgendwo anzeigen möchte (z.B. in einer GUI), im weiteren Verlauf dann ohnehin auch wieder etwas
"'gestrickte(s)'"
brauche? Dann hätte ich also nichts gewonnen?
Und inwiefern deutet dieser Ausdruck etwas über die Code-Qualität an? (ohne, dass ich mich jetzt angegriffen fühlte! Für ein "geht, ist aber nicht gut" bin ich immer offen. 🙂 )

LG an alle
 
Mit "selbstgestrickt" meinte ich einfach, das du keine "fertige" Lösung verwendest, wie das oben genannte Jackson. Es gibt für vieles in der Java-Welt bereits entsprechende Lösungen, die man nur noch einsetzen muss. Weil es einfach unnötig ist, dass die gleiche Arbeit immer wieder gemacht wird, besonders, wenn es sich um etwas kompliziertere Dinge handelt. Der Nachteil ist allerdings, dass man sich mit der richtigen Verwendung der Bibliothek beschäftigen muss, was mehr oder weniger aufwändig ist und gerade am Anfang auch überfordern kann.

Es ist generell besser, ein bekanntes Format zu wählen, als etwas vollständig selbst erfundenes. So könntest du deine Daten z. B. als CSV-File speichern. Das ist immer noch ein einfaches Textformat, aber kann zusätzlich auch von Excel gelesen werden.

Deine andere Frage habe ich nicht ganz verstanden. Durch Serialisierung kann man ein Objekt in einen Byte-Stream umwandeln und vice versa, um es beispielsweise, wie in deinem Fall, als Datei zu speichern. Das ist nur eine Möglichkeit, das zu tun, wenn auch sicherlich nicht die beste (siehe auch Vorteile, von bekannten Formaten, wie CSV, JSON, XML). Als Programmierer sollte man mehrere Optionen kennen und sich dann für die, für die jeweilige Anwendung, Beste entscheiden.

Ein schlauer Programmierer sorgt allerdings (durch eine gute Softwarearchitektur) dafür , dass er eine falsche Entscheidung leicht korrigieren kann, siehe das Beispiel oben. Das ist auch ein Teil der SOLID-Prinzipien, ergänzt durch bewährte Entwurfsmuster. Ich kann nur immer wieder empfehlen das Buch Entwurfsmuster von Kopf bis Fuß unbedingt durch zu arbeiten.
 
Zuletzt bearbeitet:
Muss ich mir mal auf meine "noch zu lernen"-Liste schreiben, gleich hinter Enums.
Das kannst du gut mit den unterschiedlichen Dateiformaten verbinden. 🙂
Java:
public enum Filetype {
    CSV, SER;
}

In der Anwendung, z. B.
Java:
    public static void main(String[] args) {

        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Hans", 42));
        personList.add(new Person("Felix", 33));

        // Map mit unterschiedlichen Implementationen für das Dateiformat
        Map<Filetype, PersonFile> personFiles = new HashMap<>();
        personFiles.put(Filetype.SER, new SerializedPersonFile("myfile.ser"));
        personFiles.put(Filetype.CSV, new CsvPersonFile("myfile.csv"));

        // als CSV speichern
        personFiles.get(Filetype.CSV).save(personList);

        // und wieder laden
        System.out.println(personFiles.get(Filetype.CSV).load());

    }
 
Ich versteh zwar noch nicht viel aber den Thread hier merke ich mir.
Für den Moment mach ich es gerade noch mit meiner Strickerei weiter, um voran zu kommen. Wenn es SOLID ist, kann ich das ja noch leicht einbringen 🙂
 
Ich steh grad auf dem Schlauch ... wenn es nur um die Zeilenoperationen geht, warum nicht einfach die Zeilenoperationen aus BufferedReader und -Writer nutzen? Pro Zeile oder über mehrere Zeilen hinweg kannst du dann immer noch nach Herzenslust parsen.

Für das fertige Produkt wäre aber die oben genannte Serialisierung sinnvoller. Als Übungsaufgabe kann man's auch gerne manuell machen.
 
Schade. Da steckt nichts Besonderes hinter dem Code. Du beschäftigst dich ja schon einige Zeit mit Java, das sollte kein Problem darstellen. Gib dem mal eine Chance. Das hilft dir sicher auch beim Stricken weiter. 🙂
Hallo,
ich habe mir das jetzt mal angeschaut. Hat gedauert, aber vergessen habe ich es nicht.
Jedenfalls noch kurz dazu: Das Serializing ist erstmal losgelöst vom Dateiformat, in welchem ich die Daten speichern will?!
Würde das nicht bedeuten, dass es quasi "universal" eingesetzt werden kann, um Bytes zu speichern und später wieder (mit einem geeigneten Programm) einzulesen?

weil dann möchte ich noch kurz fragen, was du meintest mit
Durch Serialisierung kann man ein Objekt in einen Byte-Stream umwandeln und vice versa, um es beispielsweise, wie in deinem Fall, als Datei zu speichern. Das ist nur eine Möglichkeit, das zu tun, wenn auch sicherlich nicht die beste (siehe auch Vorteile, von bekannten Formaten, wie CSV, JSON, XML). Als Programmierer sollte man mehrere Optionen kennen und sich dann für die, für die jeweilige Anwendung, Beste entscheiden.
Oder bezog ich diese Aussage auf meine .txt - Datei, in der ich die Dateien ablegen wollte und daher der Hinweis auf z.B. die .csv-Dateien?

Und gibt es da noch weitere Möglichkeiten ähnlich wie Serializing, um Dateien zu speichern? Bzw. ich bin mir sicher, dass es welche gibt 😀 Kannst du mir da Schlagwörter nennen von in der Praxis gängigen Varianten?
 

Zurück
Oben