Warum werden meine System.out.println's übersprungen?

BigMemo007

Aktives Mitglied
Hallo Leute,

als blutiger Anfänger kann ich folgendes nicht nachvollziehen:

Code:
public void actionPerformed (ActionEvent e) {
            File datei = new File("C:/Users/BigMemo/Documents/test.txt");
            
            try {
                BufferedReader leser = new BufferedReader(new FileReader(datei));
                System.out.println(leser.readLine());  //wird übersprungen, also nicht angezeigt
                System.out.println(leser.readLine());  //wird übersprungen, also nicht angezeigt
                System.out.println(leser.readLine());  //wird übersprungen, also nicht angezeigt
                
                String zeile;
                while ((zeile = leser.readLine()) != null) {
                System.out.println(zeile);
                    
                }
                leser.close();
            } catch (FileNotFoundException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            
        }   
    }

Also, die vielen - testhalber eingefügten - System.out.println(leser.readLine()); s werden nicht angezeigt. Die Konsolenausgabe beginnt direkt bei der while-Schleife. Ich hätte ein reihenweises Abarbeiten erwartet.

Ausgabe ist:
1. zeile
2. zeile
3. zeile
4. zeile
5. zeile

statt meiner erwarteten Ausgabe:
1. zeile
1. zeile
1. zeile
1. zeile
2. zeile
3. zeile
4. zeile
5. zeile


Weiteres Problem:
Bei while ((zeile = leser.readLine()) != null) {..} erkennt er, dass leser.readLine() nicht null ist, also geht er in die Schleife rein. Aber wenn ich
while (leser.readLine() ! null) {...} mache, geht die JVM nicht in die Schleife rein. Da glaubt er, das der Rückgabewert null ist.
Was ist denn hier der unterschied?

Vielen Dank schonmal
 
Aha.. Erkennt die JVM welche Zeilen sie schon ausgegeben hat und welche als nächste dran sind oder ist eine Art Zeilenzähler in der readLine() Methode eingebaut?

Hmm.. Wenn es in der while-Schleife funktioniert, dass readLine() immer in die nächste Zeile springt, dann muss das ja auch mit mehreren Sysouts hintereinander klappen. Ok. Das hab ich jetzt.

Aber, ist das nun die readLine() oder die JVM, die die Intelligenz dafür mitbringt?

Und warum muss ich als Prüfargument der while die readLine() in eine Variable packen und kann nicht direkt mit != null vergleichen?
while ((zeile = datei.readLine()) != null){} //klappt
while (datei.readLine() != null){} //klappt nicht, es gibt null zurück, so dass nicht in die Schleife gegangen wird.

Ja, vielleicht stelle ich echt dumme Fragen. Vielleicht auch zum Haareraufen. Ich bin bestimmt nicht der tallentierteste zum Programmieren. Aber ich hab Ehrgeiz und will es lernen!
 
Die Logik steckt im BufferedReader. Das sind ja auch nur Klassen, die irgendwie implementiert wurden. Den Code vom BufferedReader kann man z,B. unter http://developer.classpath.org/doc/java/io/BufferedReader-source.html finden. (Als Anfänger nicht wirklich lesbar - nur damit Du es prinzipiell gesehen hast.)

Und da erkennt man dann: Er liest Daten über einen Puffer. Und er sucht dann immer nach einem NewLine Zeichen ('\n').

Und der Code funktioniert auch:
while (datei.readLine() != null){}
Nur eben hast Du dann die Zeile nirgends gespeichert. Er liest also die Zeile aus der Datei und prüft, ob es null ist. Aber er speichert es in keiner Variablen, daher kannst Du da nicht drauf zu greifen.
Aber wenn Du z.B. sowas machst:
while (datei.readLine() != null){ counter++; }
Das wäre dann ein Beispiel: Du willst nur die Zeilen zählen. Was drin steht ist Dir egal. Funktioniert.

Und die Fragen sind ganz in Ordnung. Behalte die Neugierde und frag, was Dich so interessiert.
 
Die Logik steckt im BufferedReader. Das sind ja auch nur Klassen, die irgendwie implementiert wurden. Den Code vom BufferedReader kann man z,B. unter http://developer.classpath.org/doc/java/io/BufferedReader-source.html finden. (Als Anfänger nicht wirklich lesbar - nur damit Du es prinzipiell gesehen hast.)

Und da erkennt man dann: Er liest Daten über einen Puffer. Und er sucht dann immer nach einem NewLine Zeichen ('\n').

Und der Code funktioniert auch:
while (datei.readLine() != null){}
Nur eben hast Du dann die Zeile nirgends gespeichert. Er liest also die Zeile aus der Datei und prüft, ob es null ist. Aber er speichert es in keiner Variablen, daher kannst Du da nicht drauf zu greifen.
Aber wenn Du z.B. sowas machst:
while (datei.readLine() != null){ counter++; }
Das wäre dann ein Beispiel: Du willst nur die Zeilen zählen. Was drin steht ist Dir egal. Funktioniert.

Und die Fragen sind ganz in Ordnung. Behalte die Neugierde und frag, was Dich so interessiert.

Ok. Die readLine()-Methode vom BufferedReader merkt sich die Zeilen, die er schon renturniert hat und gibt die noch nicht renturnierte nächste aus. Das hab ich verstanden Danke.

Eine Kleinigkeit noch, dann hab ich es.

while (datei.readLine() != null){
System.out.println(datei.readLine());
}

Dieser Code müsste dann doch mir Zeile 2, 4, usw ausgeben, bis != null false ist. Die 1,3, usw wird dann in der while-Bedingung "aufgebraucht" Es wird aber sofort null augegeben. Warum? Oder spinnt meine JVM?
 
Achso, jetzt hab ich es vielleicht. Wenn er mit
while (datei.readLine() != null)
nicht darauf zugreifen kann wie du sagtest, weil eben nicht in eine Variable gespeichert, erkennt er auch nicht, ob in der Datei was drin ist und gibt per Default null zurück. Ist das so richtig?

Leider kann ich vom xioami mi9 aus den code-Feld nicht richtig nutzen, weil mir Zeilen weggelöscht werden(warum auch immer). Entschuldigt bitte, wenn ich Code direkt als Text schreibe gerade.
 
Ist das so richtig?
Fast. Es geht nicht darum, was "er" erkennt (wer auch immer "er" sein soll).

Ich fange mal von vorne an. Als erstes musst Du verstehen, was ein Datenstrom (Stream) ist.

Stell Dir ein Kassenband im Supermarkt vor. Die Teile funktionieren so, dass das Band kontinuierlich läuft. In der Nähe der Kasse ist eine Lichtschranke angebracht, die durch einen auf dem Band liegenden Artikel unterbrochen wird, damit das Band stoppt, wenn der Artikel in Griffweite des Kassierers liegt.

Jetzt nehmen wir an, dass der Kassierer so ungünstig sitzt, dass er immer nur den vordersten Artikel sehen kann. Er weiß also im Voraus nicht, ob auf dem Band noch fünf oder fünfzig weitere Artikel liegen.

Immer, wenn der Kassierer ein Artikel vom Band nimmt, kommt der nächste - bis kein weiterer Artikel mehr auf dem Band verfügbar ist.

Ein Datenstrom funktioniert wie das Kassenband. Man weiß im Voraus nicht, wann das Ende erreicht ist. So lange weitere Elemente im Strom vorhanden sind (Artikel auf dem Band liegen), kann man ein Element aus dem Strom lesen, dabei wird das Element aus dem Strom entfernt (Artikel wurde vom Band genommen). Gibt es keine weiteren Elemente im Strom, wird das entsprechend angezeigt.

Mit einem Reader liest man einen Zeichenstrom. Man kann sich das nächste Zeichen holen, dann ist das Zeichen aus dem Strom entfernt usw. Das Ende bekommt man entsprechend angezeigt (in Java liefert die read-Methode -1, wenn das letzte Zeichen des Stroms bereits gelesen wurde).

Ein BufferedReader ist in der Lage, den Zeichenstrom nicht nur zeichen- sondern auch zeilenweise zu lesen. Dann wird eben nicht ein einzelnes Zeichen, sondern eine ganze Zeile aus dem Zeichenstrom gelesen und entfernt. Wurde das Ende des Stroms erreicht, gibt readLine() null zurück.

In der Schleife
Java:
while (br.readLine() != null) {}
wird readLine() wiederholt aufgerufen. Bei jedem Aufruf wird die nächste Zeile aus dem Zeichenstrom gelesen und entfernt, bis keine weitere Zeile mehr vorhanden ist. Nachdem die letzte Zeile gelesen wurde, liefert readLine() null und die Schleife wird beendet.

Das Problem ist nun einfach, dass Du Dir weder die gelesene Zeile merkst noch in der Schleife irgendetwas sinnvolles machst. Wir könnten ja mal Zeilen zählen:
Java:
int anzahl = 0;
while (br.readLine() != null) {
    anzahl++;
}
System.out.printf("%d Zeilen gelesen\n", anzahl);

Wenn Du in der Schleife Zugriff auf die gelesene Zeile haben willst, musst sich das Programm die gelesene Zeile irgendwo merken. Dazu verwendest Du eine Variable, der Du die gelesene Zeile zuweist:
Java:
String zeile;
while ((zeile = br.readLine()) != null) {
    System.out.println(zeile);
}
oder ausführlicher:
Java:
String zeile = br.readLine();
while (zeile != null) {
    System.out.println(zeile);
    zeile = br.readLine();
}

Entschuldigt bitte, wenn ich Code direkt als Text schreibe gerade.
Das ist in dem Fall kein Problem, weil Du erstens nur eine Zeile geschrieben hast und zweitens keine Zeichen verwendet hast, die der Editor falsch interpretiert. Du kannst aber Code auch ohne Toolbar im Editor setzen, dazu einfach [code=Java]Langer Code inkl. Einrückungen[/code] in den Editor tippen. Für inline-Code verwendest Du einfach [icode]was kurzes[/icode].
 
Vielen vielen Dank an euch allen. Jetzt hab ich es gecheckt. Ich hab berufsbedingt nur zwei Tage Zeit in der Woch meinen Hobbys nachzugehen und da mache ich so viel (gerade Java), dass mir der Kopf raucht und ich einfache Sachen nicht mehr sehe. Aber eine Nacht drüber und es geht wieder.

Java:
 while (leser.readLine() != null) {
        System.out.println(leser.readLine());      
}

Jetzt wird mir Zeile 2, 4, null angezeigt, was auch korrekt ist, weil die obere leser.readLine() Zeile 1, 3, 5 verbraucht (ich habe 5 Zeilen in meiner txt-Datei). Gestern hat meine Anzeige nur null ausgegeben, sonst nichts. Ich hab bestimmt einen dummen kleinen Fehler drin gehabt, den ich zum Erbrechen nicht gesehen habe.
 

Neue Themen


Zurück
Oben