Fehlermeldung bei Aufruf von Methode

foreverqt

Mitglied
Hey kurze Frage:
wenn ich mein Programm ausführe kann ich meine Auswahl (1-4) treffen und die das Programm bittet mich darum meine Flugnummer einzugeben. Aber ab dem Moment bricht es ab und ich kann nicht mal mehr etwas eingeben obwohl ich der Meinung bin das mit dem Scanner für die Methode "flugNummer" alles richtig gemacht zu haben...

Fehlermeldung in der Konsole ist folgende:
Exception in thread "main" java.util.NoSuchElementException: No line found
at java.base/java.util.Scanner.nextLine(Scanner.java:1677)
at Aufgabe2/Aufgabe2.Main.flugNummer(Main.java:59)
at Aufgabe2/Aufgabe2.Main.ausgabe(Main.java:68)
at Aufgabe2/Aufgabe2.Main.main(Main.java:41)

Danke schonmal für die Hilfe

Code:
package Aufgabe2;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        
    String pfad ="nummerSechs.csv";
    File file = new File(pfad);
    BufferedReader bRead = null;
    int i = 0;
    String[][] flugdaten = new String[10][4];
    
    //Übernahme der Flugdaten aus .csv Datei und Speicherung in einem
    //zweidimensionalen Array
    //mittels trim() werden sofort alle unnötigen Leerzeichen entfernt,
    //sodass die Daten bereits korrekt abgelegt werden können
    try {
        bRead = new BufferedReader(new FileReader(file));
        String zeile = null;
        
        while((zeile = bRead.readLine()) != null) {
            String[] zwSpeicher = zeile.split(",");
            flugdaten[i][0] = (zwSpeicher[0].trim());
            flugdaten[i][1] = zwSpeicher[1].trim();
            flugdaten[i][2] = zwSpeicher[2].trim();
            flugdaten[i][3] = zwSpeicher[3].trim();
            i++;
            }       
        bRead.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    int eing = programmWahl();
    ausgabe(flugdaten, eing);
    
    //ausgabe(flugdaten, eing);
    }
    
    //Eingabeaufforderung und Übernahme der Parameter
    public static int programmWahl() {
        Scanner s = new Scanner(System.in);
        System.out.println("Was möchten Sie tun?");
        System.out.println("1  = Flugdaten einsehen // 2 =  Flugdaten modifizieren // 3 = Flug löschen // 4 =  Nichts, bitte abbrechen");
        int eing = s.nextInt();
        s.close();
        return eing;
        }
    
    public static String flugNummer() {
        Scanner s = new Scanner(System.in);
        System.out.println("Wie lautet die Flugnummer");
        String flugNum = s.nextLine();
        s.close();
        return flugNum;
        }
    
    public static void ausgabe(String[][] flugdaten,int eing) {
        
        switch(eing) {
        case 1:           
            System.out.println(flugNummer());
            
            break;
        case 2:
            System.out.println("2");
            break;
        case 3:
            System.out.println("3");
            break;
        case 4:
            System.out.println("4");
            break;
        default:
            System.out.println("Fehler bei der Eingabe");
            break;
        }
    }

    
    
    
}
 
Du schließt den Scanner. Damit schließt du auch System.in.

Du solltest den Scanner daher nicht schließen. Du kannst den Scanner auf System.in als Klassenvariable erstellen und dann von überall aus nutzen.
 
Danke für den Tipp, das hat meine Fehlermeldung behoben allerdings lässt er mich jetzt trotzdem nichts eintippen auf der Konsole 😀

Habe den Code folgendermaßen abgeändert

Java:
//Eingabeaufforderung und Übernahme der Parameter
    public int programmWahl() {
        System.out.println("Was möchten Sie tun?");
        System.out.println("1  = Flugdaten einsehen // 2 =  Flugdaten modifizieren // 3 = Flug löschen // 4 =  Nichts, bitte abbrechen");
        int eing = s.nextInt();
        return eing;
        }
    
    public String flugNummer() {
        System.out.println("Wie lautet die Flugnummer");
        String flugNum = s.nextLine();
        return flugNum;
        }
    
    public void ausgabe(String[][] flugdaten,int eing) {
        int i = 0;
        StringBuffer buf = new StringBuffer();
        String flugnummer = flugNummer();
        while (flugdaten[i][1] != null) {
            //System.out.println(flugdaten[i][1]);
            if(flugdaten[i][1].equals(flugnummer)) {
                for(int j = 0; j <= 3; j++) {
                    
                    buf.append(flugdaten[i][j]);
                    buf.append(" ");
                    System.out.println(flugdaten[i][j]);
                }
                
            }
            
            i++;
        }
        System.out.println(buf.toString());
        
    }
 
Das Problem ist die Art der Nutzung von Scanner. Du kannst den Scanner entweder Token basiert oder Zeilen orientiert nutzen.

nextInt ist eine Merhode, die Tokenorientiert arbeitet. Dabei werden erst mögliche Trennzeichen gelesen, dann Zeichen bis wieder ein Trennzeichen kommt. Nehmen wir das Beispiel:
Eingabe „1\n“ (\n ist der Zeilenumbruch / Enter Taste“
Ein nextInt Aufruf liest nun die 1 aber lässt das \n noch im Eingabepuffer.

Das nextLine lädt alle Zeichen einschließlich dem \n.

Was passiert nun bei einem nextInt + folgendem nextLine Aufruf?

Bei dem nextInt gibst du 1\n ein, die 1 wird gelesen und zurück gegeben. Das nextLine ließt nun das \n ein und gibt direkt den leeren String zurück.

Was ist die Lösung?
Wenn du nach einer Token basierten Eingabe eine Zeilen basierte Eingabe machen willst, musst du den Zeilenwechsel auslesen. Das kannst du mit einem einfachen nextLine Aufruf machen. Also einfach ein zusätzlichen Aufruf von nextLine hinzufügen. (Man könnte es noch etwas komplexer aufbauen, aber für die Übungen zum Java lernen sollte dies so ausreichen)
 
Ok, nur als kleine Information: Das war mal wieder unser Foren-Troll, der auf Grund seines Verhaltens immer wieder gebannt wird und dann neue Accounts erstellt. Mehr wichtige Punkte gibt es dazu nicht.

Wenn man sich aber für die Thematik etwas mehr interessiert, dann wäre hier das Folgende aus meiner Sicht zu sagen (Aber für jemanden, der Java Grundlagen erlernen will, eher uninteressant).

Der Hinweis aus der verlinken SO Antwort besagte einfach, dass man den Puffer vom Scanner nicht direkt löschen kann. Das ist klar, denn der Puffer ist halt gekapselt im Scanner und da hat man keinen direkten Zugriff drauf. ==> Diesen Puffer wollte ich aber auch nie löschen oder irgendwie direkt beeinflussen.

Aber natürlich kann man vom Scanner lesen - so kann man eine Zeile lesen. Und das wurde da auch dann im SO Thread, der verlinkt wurde, angegeben.

Der Foren-Troll "Tobias" möchte aber als Lösung vermutlich weiterhin, dass man immer neue Instanzen von Scanner erzeugt (So ich mich richtig erinnere). Also statt eine Scanner-Instanz zu erzeugen und zu nutzen, hast Du immer Aufrufe wie:
new Scanner(System.in).nextint();
Du erzeugst also eine Instanz, rufst die Methode auf und danach wird die Instanz verworfen.

Das kommt mit mehreren Problemen daher:
  • Der erste Punkt ist Clean Code / Performance: Man erzeugt nicht ständig Instanzen um diese dann sofort vom Garbage Collector wieder entsorgen zu lassen.
  • Was alles im Puffer ist, ist nicht prüfbar. Das sollte deutlich kontrollierter gehandhabt werden. (Wenn man mehrere int Werte einfordert, dann kann das ja auch direkt eingegeben werden. Also wenn zwei nextInt() Aufrufe kommen, dann kann man z.B. schreiben: "1 2" - dann gibt der erste nextInt Aufruf 1 und der Zweite dann eine 2 zurück.)
  • Clean Code: Scanner implementiert AutoCloseable - da sollte immer ein close() sichergestellt sein. Dazu gibt es dann z.B. in Java das try with resources. Aber das will man hier auch explizit nicht, da dies ja das System.in schließen würde. Das Thema wurde schon mehrfach besprochen im Forum mit Ideen wie System.in zu wrappen, das dann das close() nicht weiter gibt oder eben, dass man es als public static final in einer Klasse deklariert und damit hat man einen Lebenszyklus, der bis zum Ende der Runtime geht und man daher kein close() braucht (Stimmt nicht ganz, streng genommen kann auch eine geladene Klasse vom GC entfernt werden. Aber das ist definitiv nichts mehr für Anfänger.)

Wenn man sich dann ganz intensiv damit beschäftigen will, dann kann man mit dem Scanner auch eine deutlich bessere Lösung aufbauen. Ideen dazu:
  • Tracking, ob denn zuletzt eine Token-Basierte Methode oder eben eine Zeilen orientierte Methode aufgerufen wurde.
  • Wenn der Wechsel von Token basiert zu Zeilen basiert erfolgt, wird die Rückgabe ausgewertet: Ist diese leer, dann wird erneut die Zeile gelesen (das ist halt die einzige Zeilen basierte Methode). Ist diese nicht leer, hat man eine Eingabe auf der Zeile gehabt. (Ob man die auswerten will, muss man sich dann überlegen. Das erlaubt eine schnellere Eingabe ...)
  • Wenn man die Auswertung der ersten Rückgabe von nextLine machen will, muss man noch überlegen: Will man die Trennzeichen mit nehmen oder nicht? Also will man (ein oder die?) führende(s/n) Trennzeichen entfernen?
Hier ist also viel möglich, was man noch machen könnte. Aber es geht bei so Aufgaben doch um ein Erlernen von Java Grundlagen, und da spielt das absolut keine Rolle!
 
Der Hinweis aus der verlinken SO Antwort besagte einfach, dass man den Puffer vom Scanner nicht direkt löschen kann. Das ist klar, denn der Puffer ist halt gekapselt im Scanner und da hat man keinen direkten Zugriff drauf. ==> Diesen Puffer wollte ich aber auch nie löschen oder irgendwie direkt beeinflussen.
Was ein Designfehler des Scanners ist.

Der Foren-Troll "Tobias" möchte aber als Lösung vermutlich weiterhin, dass man immer neue Instanzen von Scanner erzeugt
Wieso nimmst du an, dass er das möchte?

Der erste Punkt ist Clean Code / Performance: Man erzeugt nicht ständig Instanzen um diese dann sofort vom Garbage Collector wieder entsorgen zu lassen.
Performance bei Benutzereingaben?
 
Ach Tobias,

Was ein Designfehler des Scanners ist.
Die Kapselung von Daten in der Klasse Scanner ist ein Designfehler?

Wieso nimmst du an, dass er das möchte?
Du hast selbst behauptet, in der Vergangenheit etwas zu Scannern geschrieben zu haben … Ich habe nur versucht, mich zu erinnern.,,.


Performance bei Benutzereingaben?
System.in ist das stdin der Anwendung - das müssen nicht Benutzereingaben sein.

Und bei Clean Code geht es um das Prinzip.

Ich habe Deine Antwort mal nicht mit gelöscht, da die eine oder Andere Anfängerfrage durchaus interessant sein kann …
 
Und was genau ist ein Mixed-Mode in diesem Kontext? - Und warum kann ich für den gelegentlichen Einsatz nicht mit dem Scanner leben?

Also irgendwie bedarf dieser Post einer weiteren Erklärung. Vor allem gerade dann, wenn man sich extra deswegen hier anmeldet.
 
Das Scanner sowohl Token- als auch Zeilenbasiert benutzt werden kann, ist durchaus eine Thematik, die eine gewisse Komplexität mit bringt. Aber ich sehe da nicht wirklich eine gute Lösung.

Was man machen könnte, wäre halt, auch nach einem Token die Trennzeichen zu löschen.

Aber das bringt dann ein Problem mit, dass dies nur schwer möglich ist.

Trennzeichen kann (u.a.) Leerzeichen und Zeilenumbruch sein.

Wenn ich nun eingebe: 3\n \n3\n
Trennzeichen sind hier zwischen den beiden 3 halt Zeilenumbruch, Leerzeichen, Zeilenumbruch.
zwei nextInt Aufrufe würden beide eine 3 zurück geben.
Was soll nextInt, nextLine zurückgeben?
Wenn nach einem Token alle Trennzeichen entfernt würden, wäre die nextLine Rückgabe die 3
Aber wie bekommt man dann bei nextLine denn leeren String oder den String mit Trennzeichen?

Also wäre die Regel, dass da der Zeilenumbruch besonders berücksichtigt würde. Also eine Reglung a.la. „es werden Trennzeichen entfernt bis ein Zeilenumbruch erreicht wurde.

Aber die Trennzeichen lassen sich ja vorgeben. Was, wenn der Zeilenumbruch kein Trennzeichen mehr ist?

Die Komplexität wäre somit aus meiner Sicht deutlich größer.

Es wäre also denkbar, die Klasse zu teilen; Token basierter Scanner und Zeilen orientierter Scanner.
Beim ersten fehlt dann ggf. eine Methode zum Cleanup. Wenn man eine Zahl braucht und keine Zahl eingegeben wurde, dann Willmann bei Benutzereingaben die letzte Eingabe löschen (das ist halt eine wichtige Nutzungsmöglichkeit).
Und zweites gibt es - siehe Reader Implementierungen.

Denkbar wäre, dass man Scanner rein für Eingaben verwendet - pro Zeile eine Eingabe. Für Token basierte Auswertung mit beliebigen Trennzeichen wäre dann ggf. eine Klasse TokenReader oder so denkbar …

Aber die Diskussion ist wenig zielführend - die Klasse ist nun einmal so und ich sehe keine wirklichen Gründe für Veränderungen.
 

Zurück
Oben