Wortliste in einer Datei ablegen

melaniemueller

Aktives Mitglied
Guten Abend zusammen,
ich hänge seit 2 Wochen an einer Aufgabe und komme nicht weiter und hab das Gefühl ich habs komplett falsch verstanden/gemacht.
Ich hab gesehen es gab diese Aufgabe im Forum schon öfter. Hab ich mir auch durchgelesen aber hat mir nicht wirklich weiter geholfen, daher schon mal sorry falls es dem ein oder anderen hier schon bekannt vor kommt.

Folgende Aufgabe:
Erstellen Sie einen Editor, mit dem Sie eine Wortliste für das Hangman-Spiel in einer Datei ablegen können. Speichern Sie in dieser Datei nicht nur die Wörter an sich, sondern auch die Anzahl der gespeicherten Wörter.

Für das Speichern reicht es aus, wenn Sie jeweils ein neues Wort am Ende der Datei anhängen. Eine nachträgliche Änderung von gespeicherten Wörtern ist nicht erforderlich. Der Editor soll aber die bereits vorhandenen Wörter anzeigen können.

Wie Sie den Editor im Detail umsetzen und welche Bibliothek Sie für die Oberfläche verwenden, bleibt Ihnen überlassen. Dokumentieren Sie Ihre Lösung aber bitte ausführlich.


So nun mein bisheriger Code dazu:
Java:
package wörterliste;

import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.RandomAccessFile;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class Wörterliste extends JFrame {
    private static final long serialVersionUID = 6468516705291496250L;
    private JTextField feld;
    private JLabel wörter, anzahl;
    private JButton laden, beenden, hinzufügen;
    private int anzahlWoerter;
    
    private JPanel panelTexteingabe() {
        JPanel tempPanel = new JPanel();
        JLabel ausgabe = new JLabel("Worteingabe:");
        feld = new JTextField(20);
        tempPanel.setLayout(new GridLayout(0,2));
        tempPanel.add(ausgabe);
        tempPanel.add(feld);
        return tempPanel;
    }
    private JPanel panelDatenAusgabe() {
        JPanel tempPanel = new JPanel();
        anzahl = new JLabel("Anzahl der Wörter: ");
        wörter = new JLabel("Gespeicherte Wörter: ");
        tempPanel.setLayout(new GridLayout(2,0));
        tempPanel.add(anzahl);
        tempPanel.add(wörter);
        return tempPanel;
    }
    
    private JPanel panelButtonsErstellen() {
        JPanel tempPanel = new JPanel();
        laden = new JButton("Gespeicherte Daten laden");
        laden.setActionCommand("lesen");
        hinzufügen = new JButton("Neues Wort hinzufügen");
        hinzufügen.setActionCommand("speichern");
        beenden = new JButton("Beenden");
        beenden.setActionCommand("ende");
        tempPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
        tempPanel.add(laden);
        tempPanel.add(hinzufügen);
        tempPanel.add(beenden);
        return tempPanel;
    }

    class MeinListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getActionCommand().equals("lesen"))
                datenLesen();
            if (e.getActionCommand().equals("hinzufügen"))
                datenSchreiben();
            if (e.getActionCommand().equals("ende"))
                System.exit(0);
        }
    }

    // der Konstruktor
    public Wörterliste(String titel) {
        super(titel);
        JPanel panelText, panelDaten, panelButtons;
        panelText = panelTexteingabe();
        panelDaten = panelDatenAusgabe();
        panelButtons = panelButtonsErstellen();
        MeinListener listener = new MeinListener();
        laden.addActionListener(listener);
        hinzufügen.addActionListener(listener);
        beenden.addActionListener(listener);
        setLayout(new GridLayout(3,0));
        add(panelText);
        add(panelDaten);
        add(panelButtons);
        pack();
        setVisible(true);
    }

    private void datenLesen() {

        try(RandomAccessFile datei = new RandomAccessFile("liste.bin", "r")) {
            String tempWörter;
            int tempAnzahl;
            for (int index = 0; index < anzahlWoerter; index++) {
                tempAnzahl = datei.readInt();
                tempWörter = datei.readUTF();
            }
        }
        catch (IOException e) {
            JOptionPane.showMessageDialog(this,  "Beim Laden ist ein Fehler aufgetreten");
        }
    }

    private void datenSchreiben() {
        try (RandomAccessFile datei = new RandomAccessFile("liste.bin", "rw")) {
            if(feld.getText().equals(""))
            {   
                JOptionPane.showMessageDialog(null, "Sie haben noch kein Wort eingegeben");
            }
            else
            {
                anzahlWoerter++;
                datei.writeInt(anzahlWoerter);
                datei.seek(datei.length());
                datei.writeUTF(feld.getText());
                //Textfeld leeren
                feld.setText("");
            }
        }
        catch (IOException e)
        {
            JOptionPane.showMessageDialog(this, "Beim Schreiben ist ein Problem aufgetreten");
        }   
    }
}

Die Oberfläche an sich gefällt mir bisher eigentlich ganz gut. Aber nun hab ich die fertig gestellt und frage mich ob mein TextField überhaupt richtig ist oder ob es wenn von einem Editor die Rede ist ich TextArea wählen müsste? Dann hab ich es ja so erstellt das darunter Labels angezeigt werden in welchen die Wörter sowie Anzahl angezeigt werden soll. Wird also nicht im Editor angezeigt. Habs aber auch überhaupt nicht hingebracht dass die gespeicherten Wörter in den Labels angezeigt werden. Wusst nicht so recht wie. Nun hab ich ewig daran gearbeitet und schätze fast umsonst. Wie ist eure Meinung? Was ist falsch was könnte ich machen?

Bin für jeden Tipp dankbar!
 
canvas.png
konnte nicht widerstehen 😀

ich weis nicht wie oft das schon durch gekaut wurde dass man Berechnende Logik ( auch "models" genannt ) , Ablauf des Programmes bzw steuerung , und die Grafik trennen sollte

und dass man zuerst die Berechnende Logik shcreibt ... und dann die GUI

mach das zuerst weil du dich wahrschienlich selbst nicht mehr auskennst
 
Anhang anzeigen 17678
konnte nicht widerstehen 😀

ich weis nicht wie oft das schon durch gekaut wurde dass man Berechnende Logik ( auch "models" genannt ) , Ablauf des Programmes bzw steuerung , und die Grafik trennen sollte

und dass man zuerst die Berechnende Logik shcreibt ... und dann die GUI

mach das zuerst weil du dich wahrschienlich selbst nicht mehr auskennst
Ups da hast du wohl recht. Danke, damit werd ich gleich mal anfangen.
 
ich weis nicht wie oft das schon durch gekaut wurde dass man Berechnende Logik ( auch "models" genannt ) , Ablauf des Programmes bzw steuerung , und die Grafik trennen sollte

Das spielt doch fuer so eine Uebungsaufgabe am Anfang keine Rolle, insbesondere dann nicht wenn man sie noch nicht am Laufen hat. Auftrennen und mit der Aufteilung experimentieren kann man wenn das Ding laeuft. Und selbst dann sollte man klar machen wo man ansetzen kann.

Aber nun hab ich die fertig gestellt und frage mich ob mein TextField überhaupt richtig ist oder ob es wenn von einem Editor die Rede ist ich TextArea wählen müsste?

Ein Editor ist ein Editor...da behaupte ich mal es kann alles sein. Ein einfaches Textfeld passt da schon.

Habs aber auch überhaupt nicht hingebracht dass die gespeicherten Wörter in den Labels angezeigt werden. Wusst nicht so recht wie. Nun hab ich ewig daran gearbeitet und schätze fast umsonst. Wie ist eure Meinung? Was ist falsch was könnte ich machen?

Du hast eine handvoll Probleme in dem Programm.

Das kleinste ist, dass du keine setDefaultCloseOperation gesetzt hast, also wenn man das Fenster einfach schlieszt, bleibt der Prozess am laufen.

Dann, deine Button-Listener Logik erwartet den ActionCommand "hinzufügen", der Button dazu hat aber "speichern" gesetzt.

Dann machst du dir das Leben relativ schwer mit einem RandomAccessFile. Da du hier einfach nur Text-Dateien schreiben musst, welche noch dazu sehr gut definiert sind in ihrere maximalen Groesze, wuerde ich vorschlagen du liest immer die gesamte Datei und schreibst die gesamte Datei als Text. Dafuer gibt es im java.nio-Paket wirklich viele hilfreiche Funktionen. Als schnelle und dreckige Loesung wuerde ich die Datei als Liste an Zeilen lesen, und auch als solche schreiben. Also dein Model wird dann eifnach eine List<String> sein, wo der erste Eintrag die Anzahl an Woertern ist, und alle anderen die Woerter selbst. In etwa so:

Java:
Path wortlisteDatei = Paths.get("./liste.text");

List<String> wortlisteDateiInhalt = Files.readAllLines(wortlisteDatei);

Jetzt hast du hier eine Liste nach Zeilen in der Datei. Die erste Zeile ist immer die Anzahl, alles danach sind Woerter. Wenn du also ein Wort hinzufuegen willst, musst du nur folgendes machen:

Java:
Path wortlisteDatei = Paths.get("./liste.text");

List<String> wortlisteDateiInhalt = Files.readAllLines(wortlisteDatei);

// Wort hinzufuegen
wortlisteDateiInhalt.add(feld.getText());

int anzahlWoerter = wortlisteDateiInhalt.size() - 1; // 1 abziehen weil die erste Zeile ja die Anzahl ist.

// Anzahl auf Stand bringen
wortlisteDateiInhalt.set(0, Integer.toString(anzahlWoerter));

Files.write(wortlisteDatei, wortlisteDateiInhalt);

Du musst nur beachten wenn es die Datei noch nicht gibt, in etwa so:

Java:
Path wortlisteDatei = Paths.get("./liste.text");

List<String> wortlisteDateiInhalt = null;

if (files.exists(wortlisteDatei)) {
    wortlisteDateiInhalt = Files.readAllLines(wortlisteDatei);
} else {
    wortlisteDateiInhalt = new ArrayList<>();
    wortlisteDateiInhalt.add("0");
}

Und wenn du dann die Woerter anzeigen willst, musst du einfach nur jede Zeile anzeigen in dieser Liste.

Soweit so klar?
 
In der Aufgabenstellung ist die Rede davon, dass nur ein Wort an das Ende der Datei geschrieben werden soll. Dafür sollte ein JTextfeld ausreichen.
Die Wörter werden nie gespeichert weil:
Java:
this.hinzufügen.setActionCommand("speichern");
Java:
        public void actionPerformed(final ActionEvent e) {
            if ("lesen".equals(e.getActionCommand())) {
                datenLesen();
            }
            if ("hinzufügen".equals(e.getActionCommand())) {
                datenSchreiben();
            }
            if ("ende".equals(e.getActionCommand())) {
                System.exit(0);
            }
        }
Jetzt ist natürlich "speichern" ungleich "hinzufügen", somit wird datenSchreiben(); nie aufgerufen.
 
ActionListener ist ein Interface mit nur einer Methode, daher kannst du einfach folgendes schreiben:
Java:
this.hinzufügen.addActionListener(e -> datenSchreiben());
Anderfalls solltest du Konstanten benutzen:
Java:
private static final String CMD_SAVE = "speichern";
Dann solltest du überall die Konstanten verwenden. So ist das Problem von Schreibfehlern in Strings ausgeschlossen.
Java:
this.hinzufügen.setActionCommand(CMD_SAVE);
Java:
    class MeinListener implements ActionListener {
        @Override
        public void actionPerformed(final ActionEvent e) {
            if ("lesen".equals(e.getActionCommand())) {
                datenLesen();
            }
            if (CMD_SAVE.equals(e.getActionCommand())) {
                datenSchreiben();
            }
            if ("ende".equals(e.getActionCommand())) {
                System.exit(0);
            }
        }
    }
 
In der Aufgabenstellung ist die Rede davon, dass nur ein Wort an das Ende der Datei geschrieben werden soll. Dafür sollte ein JTextfeld ausreichen.
Die Wörter werden nie gespeichert weil:
Java:
this.hinzufügen.setActionCommand("speichern");
Java:
        public void actionPerformed(final ActionEvent e) {
            if ("lesen".equals(e.getActionCommand())) {
                datenLesen();
            }
            if ("hinzufügen".equals(e.getActionCommand())) {
                datenSchreiben();
            }
            if ("ende".equals(e.getActionCommand())) {
                System.exit(0);
            }
        }
Jetzt ist natürlich "speichern" ungleich "hinzufügen", somit wird datenSchreiben(); nie aufgerufen.
Danke der Fehler ist mir gar nicht aufgefallen, so kanns natürlich nicht funktionieren. Das hab ich nun direkt mal ausgebessert.
Dann machst du dir das Leben relativ schwer mit einem RandomAccessFile. Da du hier einfach nur Text-Dateien schreiben musst, welche noch dazu sehr gut definiert sind in ihrere maximalen Groesze, wuerde ich vorschlagen du liest immer die gesamte Datei und schreibst die gesamte Datei als Text. Dafuer gibt es im java.nio-Paket wirklich viele hilfreiche Funktionen. Als schnelle und dreckige Loesung wuerde ich die Datei als Liste an Zeilen lesen, und auch als solche schreiben. Also dein Model wird dann eifnach eine List<String> sein, wo der erste Eintrag die Anzahl an Woertern ist, und alle anderen die Woerter selbst. In etwa so:
RandomAccessFile verwende ich weil die Übungen im jeweiligen Heft auch meist damit gemacht wurden. Daher dachte ich ist es so erwünscht. Mit einer Liste wurde aber auch mal gearbeitet. Path musste ich erstmal nachlesen das hat mir gar nichts gesagt. Dein Weg klingt aber wesentlich unkomplizierter. Ich werds mal so versuchen umzusetzen.
 
RandomAccessFile verwende ich weil die Übungen im jeweiligen Heft auch meist damit gemacht wurden. Daher dachte ich ist es so erwünscht.

So etwas hatte ich mir schon gedacht, aber dafuer war mir die Aufgabenstellung zu wenig spezifisch auf die Notwendigkeit die Datei an bestimmten Stellen zu betreten.

Man muss aber auch immer beachten dass es noch genuegend Uebungen gibt die aus pre-Java 8 Zeiten stammen, und damit viele neue Teile der Standardbibliothek gar nicht verwenden, zum Beispiel java.nio, oder LocaleDateTime/OffsetDateTime, oder Lambdas und Methodenreferenzen. Da muss man dann versuchen zu verstehen was die Intention der Uebung war.
 
um das mal zu erwähnen ... java 8 war ein riesen patch für java... viele neue sachen .. wie zb Generics ... und dann die ganze standard biibliothek gefühlt überarbeitet... sollte man schon aufpassen
 

Zurück
Oben