zufälliges Wort aus einer Datei einlesen

Ollson70

Mitglied
Hallo,
ich habe folgendes problem mit dem u.a. Quellcode. Die Aufgabe ist es ein ein zufälliges Wort aus einer Datei einzulesen.
Habe schon alles mögliche versucht. Das einlesen klappt auch soweit. Leider lieste er immer nur das erste Wort der datei ein.Weiß nicht mehr weiter.
oder ist es gar der völlig falsche ansatz.

//die Methode ermittelt zufällig ein Wort
private void neuesWort() {
//Aufgabe2 das einlesen der Worte aus der datei woerter.dat
try (RandomAccessFile datei = new RandomAccessFile("woerter.dat","r")){


int zufall;
//String [] woerter = {"Test", "Automobil", "Versuch", "Hund", "Katze",
//"Ziege", "Maus", "Elefant", "Isopropylalkohol", "Schwimmbad"};
//woerter = datei.readUTF();



//ein zufälliges Wort ermitteln

zufall = (int)(datei.length()*Math.random());

// die schleife für die durchläufe
//while(datei.getFilePointer() < datei.length()) {
for(int i = 0; i< zufall; i++);

// und die Position für das Wort holen
long neuePosition= zufall;
//und das wort weitergeben
zufall = datei.readUnsignedShort();
//datei.seek(datei.getFilePointer() + neuePosition);
datei.skipBytes((int) (datei.getFilePointer() + zufall));
//String wort = datei.readUTF();



//das Suchwort und die Zeichen für die Anzeige setzen
suchwort = new String(datei.readUTF());
//suchwort = new String(datei.readUTF());
anzeige = new StringBuilder(suchwort);

//alle Zeichen in der Anzeige ersetzen durch *
for (int zeichen = 0; zeichen < suchwort.length(); zeichen++)
anzeige.setCharAt(zeichen, '*');

//die Sternchen anzeigen
ausgabeText.setText(anzeige.toString());

// }
}
catch(IOException e) {
Alert meinDialog = new Alert(AlertType.INFORMATION, "Beim Laden ist ein Problem aufgetreten");
meinDialog.setHeaderText("Bitte beachten");
meinDialog.showAndWait();
}
}
 
Das Problem bei deinem Ansatz ist, dass Wörter, welche länger sind, eine größere Chance haben gewählt zu werden. Ist das gewünscht? Eine andere Variante wäre es erst alle Wörter zu laden und dann zufällig eins auszuwählen. Das würde hingegen zu lasten des Arbeitsspeichers gehen und ist nur bei kleineren Dateien ratsam.

Ein Kompromiss wäre es die Datei 2mal zu lesen. Beim ersten Durchlauf zählst du die Wörter, dann wählst du einen zufälligen Index in diesem Rahmen und dann liest du die Datei wieder bis zu inklusive diesem Wort.
 
Irgendwie ergibt doch der ganze Code überhaupt gar keinen Sinn?... Er springt da Byte-weise durch eine Datei und erwartet, bei jedem der möglichen/zufälligen Byte-Positionen in der Datei ein gültiges Wort lesen zu können?
Mal zusammengedampft auf alles das, was nicht gerade auskommentiert ist, um den Code auch lesen zu können (plus Entfernen der unnötigen for-Schleife ohne Body):
Java:
try (RandomAccessFile datei = new RandomAccessFile("woerter.dat","r")){
  int zufall = (int)(datei.length() * Math.random()); // zufällige BYTE-Position in der Datei
  long neuePosition= zufall; // <- merken
  zufall = datei.readUnsignedShort(); // 2 Byte lesen
  datei.skipBytes((int) (datei.getFilePointer() + zufall)); // 2+zufall Bytes überspringen. Der Cursor befindet sich nun also bei 4+zufall
  suchwort = new String(datei.readUTF()); // <- hier soll jetzt ein "Wort" stehen (UTF-8 String: 2-bytes Länge gefolgt von UTF-8 Bytes)
  anzeige = new StringBuilder(suchwort);
  for (int zeichen = 0; zeichen < suchwort.length(); zeichen++)
    anzeige.setCharAt(zeichen, '*');
  ausgabeText.setText(anzeige.toString());
}
Vielleicht sollte man erstmal klären, was denn das Dateiformat eigentlich ist. Textzeilen? Binärformat?
Und was genau unter einem "Wort" verstanden wird.
 
In dem Fall ist das Problem ja dann, dass du für eine gegebene (unveränderte) Datei immer exakt dieselbe Byte-Position liest, die sich aus den ersten zwei Byte des Dateiinhaltes ergibt:
Java:
zufall = datei.readUnsignedShort(); // 2 Byte lesen (von Position 0 der Datei)
datei.skipBytes((int) (datei.getFilePointer() + zufall)); // `2 + gelesenes UShort` Bytes überspringen.
suchwort = new String(datei.readUTF());
Da ist also kein Zufall drin.
 
Und: Theoretisch kann ein zufälliger Prozess auch immer denselben Wert wie deine Methode liefern, nur ist die Wahrscheinlichkeit dafür halt sehr sehr klein. Aber möglich! 🙂 Zufällig bedeutet ja nicht gleichverteilt oder über eine endliche Menge an Versuchen gleichverteilt.
 
Ach was. Das zeigt, wie unerfahren Ihr seid! Ihr habt absolut keine Ahnung von TEAM-Arbeit!

Toll Ein Anderer Machts - wer darin gut ist, der hat es gemeistert, in Foren wie hier so zu fragen, dass Andere die Arbeit erledigen.
Das ist ein TEAM Forum hier. Anfänger erkennt man daran, dass die dann die #300 voll kriegen. Aber es kommen auch viele Experten - keine 20 Posts und die Lösung wurde schon präsentiert ... Also das ihr hier immer noch glaubt, das es um Java gehen würde ... tz tz tz 🙂

Profiler Suzanne Grieger-Langer hatte da mal ein interessantes Video zu veröffentlicht. Da wurde das vom Prinzip her auch schön aufgezeigt. Aber ich finde es gerade nicht auf Anhieb wieder ...
 
Danke an alle. Einige Antworten hätte man sich auch sparen können.
Ich fine es schade, dass einem Anfänger so viel Mist erzählt wir. Ich finde, das auf eine Anfrage auch eine vernünftige Antwort zu erwarten ist.
Nochmals vielen Dank.
 
Ich fine es schade, dass einem Anfänger so viel Mist erzählt wir. Ich finde, das auf eine Anfrage auch eine vernünftige Antwort zu erwarten ist.
Du hast doch drei vernünftige Antworten bekommen, die Probleme Deines Codes aufgezeigt haben. Für den Käse ab #6 habe ich mich ja schon vorab entschuldigt; dass das eine gewisse Eigendynamik entwickelt, ist hier normal 🙂

Du solltest vielleicht mal auf #5 antworten.
 
In dem Fall ist das Problem ja dann, dass du für eine gegebene (unveränderte) Datei immer exakt dieselbe Byte-Position liest, die sich aus den ersten zwei Byte des Dateiinhaltes ergibt:
Java:
zufall = datei.readUnsignedShort(); // 2 Byte lesen (von Position 0 der Datei)
datei.skipBytes((int) (datei.getFilePointer() + zufall)); // `2 + gelesenes UShort` Bytes überspringen.
suchwort = new String(datei.readUTF());
Da ist also kein Zufall drin.
Hallo,
Danke für die Antwort, wenn ich das richtig verstanden habe, ist es nicht möglich zufällig ein Wort aus einer Binärendatei zu bekommen. Sollte die
Worte in einer Textdatei hinterlegt sein würde es klappen. Gut, dann ändere ich meinen Editor entsprechend und versuche es mal damit.
 
wenn ich das richtig verstanden habe, ist es nicht möglich zufällig ein Wort aus einer Binärendatei zu bekommen.
Das hat hier niemand behauptet. Du tatest es mit deinem gezeigten Code nur nicht.

Lies dir nochmal die Antwort von @MoxxiManagarm durch: https://www.java-forum.org/thema/zufaelliges-wort-aus-einer-datei-einlesen.188667/#post-1224472

Da die Wörter/Strings immer unterschiedlich lang sind und an unterschiedlichen Offsets in der Datei stehen, musst du halt erstmal wissen, wieviele Wörter in der Datei sind und an welchen Byte-Offsets diese starten. Das kannst du halt machen, indem du einmal die Datei sequenziell einliest. Das ist ja möglich, weil jeweils zwei aufeinander folgende Wörter durch eine 2-Byte Länge getrennt sind und du somit weisst, wie lang jeweils das aktuelle Wort am aktuellen Cursor in der Datei ist.
Oder, du änderst das Format, indem du am Anfang der Datei einen Index pfegst, wo eben nur für jedes Wort sein Offset gespeichert ist.
 
Was auch funktionieren würde: bei einem zufälligen Byte-Offset starten, dann weiterlesen, bis zu einem Längenbyte (<65) 🙂 (EDIT: das entspricht dem, was @MoxxiManagarm eingangs meinte).
 
Zuletzt bearbeitet:
Danke für die Antwort, wenn ich das richtig verstanden habe, ist es nicht möglich zufällig ein Wort aus einer Binärendatei zu bekommen. Sollte die
Worte in einer Textdatei hinterlegt sein würde es klappen.
Was ist für dich der Unterschied zwischen einer Binärdatei, in der Text steht, und einer Textdatei? 😵
 
Hallo zusammen,
ich sitze gerade auch an diesem Problem, ein zufälliges Wort aus einer *.bin Datei zu lesen.
Die Datei ist wie folgt formatiert. Am Anfang steht ein Int wert (der Wortzähler) dieser wird bei hinzufügen von Wörtern überschrieben / verändert.
Ab da beginnen die eigentlichen Wörter jeweils mit einem * als Trennzeichen. Also zb. 5*Test*Milch*Hund*Katze*Maus.
Mir fehlt die zündende Idee wie ich das am besten lösen kann um ein einzelnes Wort auszugeben.
Es sollte mit der Klasse RandomAccessFile gelöst werden da diese gerad das Thema in diesem Studienheft ist. Weitere Beschränkungen gibt es nicht. Achso doch eine, das wort soll nicht aus einem Array entnommen werden sondern direkt aus der Liste.

Bin für Tipps sehr Dankbar, stehe Momentan direkt auf der Leitung.......

gruss Olli
 
Das Format ist für ein RAF etwas seltsam, weil es einen wahlfreien Zugriff verhindert. Somit macht es keinen Unterschied, ob RAF oder Stream, die Datei hat das Format
Code:
<token>*<token>*<token>*
und damit kannst Du eine Methode String nextToken() (oder wie auch immer man sie bezeichnen mag) schreiben, die das nächste Token zurückliefert. Das erste Token kannst Du dann als Integer parsen.

Was mit RAF allerdings funktioniert: man kann einmalig die Positionen und Länge der Token auslesen und merken (sprich: einen Zugriffspfad/Index erstellen) und dann natürlich wahlfrei auf ein bestimmtes Token zugreifen.

Nachtrag: ich sehe gerade, dass es hier ja schon entsprechende Antworten gab...
 
Hallo mihe7,
danke für deine Tips. Also ist der "beste" Weg das erstellen des Indexes in einer Art Array woraus ich dann per zufall eine Position ziehen an der das Wort steht. Habe ich das so richtig verstanden?
gruss Olli
 
So ich denke ich habe die Lösung gefunden. Es funktioniert so wie es soll. ich glaube aber ich habe den Code zu umständlich geschrieben.
[CODE lang="java" title="Methode um ein zufälliges Wort aus einer *.bin Datei zu nutzen"] private void neuesWort() {
int zufall = 0;
try(RandomAccessFile datei = new RandomAccessFile(dateiName,"r")){

int index =0;
Random random = new Random();
// beschaffen wieviele Wörter in der Datei enthalten sind
int anzahlWoerter = datei.readInt();
// die gesamte Datei durchlaufen
while(datei.getFilePointer() < datei.length()) {
// wenn das zeichen gefunden wird
if(datei.readChar() == '*') {
// zähler hochzählen
index ++;
// und das dazugehörige Wort auslesen
String wort = (datei.readUTF());
// und in das Array eintragen
woerter[index] = wort;
}
}
// zufallszahl bestimmen mit begrenzung wieviele Wörter eigentlich vorhanden sind
zufall = 1 + random.nextInt(anzahlWoerter);
if(zufall < index)
suchwort = new String(woerter[zufall]);

// Kontrollausgabe zum debuggen --> wird noch gelöscht
System.out.println(woerter[zufall]);
}
catch(IOException e) {
JOptionPane.showMessageDialog(null, "Beim Lesen der datei ist ein Fehler aufgetreten.");
}
// das Suchwort und die Zeichen für die Anzeige setzen
anzeige = new StringBuilder(suchwort);

// alle Zeichen der Anzeige durch * ersetzen
for (int zeichen = 0; zeichen < suchwort.length();zeichen ++) {
anzeige.setCharAt(zeichen, '*');
}
// ausgabe setzen
ausgabeText.setText(anzeige.toString());
}[/CODE]
 
Das Array wird auserhalb der Methode vereinbart. Die zu verarbeitende Datei ist auf 20 Wörter begrenzt. Mehr können in dem dazugehörigen Editor nicht angelegt werden. So sollte es eigentlich ja passen. Aber ist das wirklich der schlankeste Weg? Für mich sieht das sehr umständlich aus.

gruss Olli
 
Achso doch eine, das wort soll nicht aus einem Array entnommen werden sondern direkt aus der Liste.
Äh:
suchwort = new String(woerter[zufall]);

Ich würde eine Methode schreiben, die einfach nur das nächste Wort aus der Datei zurückgibt (oder z. B. null, falls ein solches nicht existiert). Dann kannst Du ganz einfach eine zufällige Zahl an Wörter (über)lesen und bist fertig.

Jetzt noch zur Geschichte mit dem Index: das ist eine Optimierung, die erst dann wirklich sinnvoll ist, wenn Du mehrfach beliebige Wörter lesen musst. Bei so einer Übungsaufgabe ist es relativ wurscht, ob der Spaß 5 Minuten oder 5 Sekunden läuft.

Um aber den Sinn eines Index etwas zu verdeutlichen, kann man ein wenig rechnen. Nehmen wir an, für jedes Wort in der Liste werden nur die Buchstaben A-Z verwendet, die durchschnittliche Wortlänge betrage 5. Dann befinden sich zwei Bytes für die Wortlänge, 5 Bytes für das Wort und zwei Bytes für das Trennzeichen je Wort in der Datei. Macht also durchschnittlich 9 Bytes/Wort. Bei 1 Mio. Wörtern hat die Wortliste eine Größe von 9 MB.

Aus diesen 1 Mio. Wörtern sollen nun 100.000 zufällig gewählte Wörter gelesen werden. Im Durchschnitt liest Du je Wort die Hälfte der Datei, also 4,5 MB bis zum gesuchten Wort, das 100.000-mal, macht 450 GB an Daten, die insgesamt verarbeitet werden.

Legen wir jetzt einen Index an, sagen wir in einer Datei, dann besteht dieser aus 1 Mio. Einträgen je 4 Bytes, die die Position der Wörter darstellen. D. h. wir lesen einmal 9 MB Wörter und schreiben einen 4 MB großen Index.

Um nun das n-te Wort zu lesen, berechnen wir die Position im Index (=4*n), springen an diese Stelle in der Datei und lesen 4 Bytes ein, die die Position in der Wortliste angeben. An diese Position springen wir in der Wortliste und lesen das Wort (durchschnittlich 7 Bytes). Pro gesuchtem Wort lesen wir also etwa 11 Bytes. Das 100.000 mal, macht 1,1 MB zzgl. der einmalig gelesenen 9 MB und den 4 MB geschriebenen Index. Insgesamt verarbeiten wir somit 14,1 MB statt 450 GB.

Das ist natürlich nicht die ganze Wahrheit, da wir Caches, Seekzeiten (insb. bei Platten) unberücksichtigt lassen, aber die Idee ist hoffentlich klar. Lösen lässt sich das Problem mit und ohne Index. Mit Index wird die Laufzeit ggf. verkürzt, ohne dauert es halt einfach länger.
 
Achso doch eine, das wort soll nicht aus einem Array entnommen werden sondern direkt aus der Liste.
Ja das war mein Fehler, wer lesen kann ist klar im Vorteil. Ich war so fixiert auf dei Problemlösung das ich die Aufgabenstellung nicht richtig gelesen habe. Da stand geschrieben " Das Wort soll aus der Datei kommen und nicht aus dem bestehenden Array" und ich habe meine Lektion gelernt in bezug auf die Vorbereitung der Aufgabenlösung. Erst lesen und ggf. Stichpunkte herausschreiben. Bedingungen und ggf Vorgaben exakt benennen und aufgliedern und dann beginnen mit dem Code schreiben.
Danke für deine Ausführung und Erläuterung. In dieser Hinsicht finde ich die Fernstudiengänge noch nicht ganz optimal, meiner Meinung nach wird da zu wenig auf Code Qualität geachtet und auch nichts in dieser Problematik geschult. Deshalb bin ich unwahrscheinlich Dankbar für solche Ausführungen / Erklärungen weil sie eben eine andere Sichtweise zeigen.

gruss Olli
 
Ich werf mal meine Noob idee rein:
Ausgehend von der 2 mal Durchgehen Methode würde ich eine wordcount(String text) methode machen, die
0 zurückgibt wenn es gar kein zeichen gibt
1 zurückgibt wenn es mindestens ein zeichen, aber nicht leerzeichen+buchstabe gibt.
und die ansosnten einfach zählt wie oft leerzeichen+buchstabe vorkommt.
dieser wert+1 zurückgegeben und ist dann die anzahl an wörtern.

hier sei angemerkt dass ein "buchstabe" alles meint, was kein leerzeichen ist.
damit dürften also auch zahlen, sonderzeichen und ähnliches hinreichend abgedeckt sein 🙂

Und dann im zweiten durchgang gehts ebenso:
wenn wortmenge=0: hast du ein problem 🙂
wortmenge=1: gib den kompletten text zurück.
wortmenge=k: gib das k-1'te wort zurück. dieses beginnt eben beim k-1 ten auftreten von leerzeichen+buchstabe
und endet entweder beim nächsten leerzeichen oder wenn der text zu ende ist.

das k produziert dir natürlich der zufallsgenerator+etwas mathe 🙂
 

Neue Themen


Zurück
Oben