C++ getch() eingegebenes Zeichen löschen



  • Das klassische getch() echoed eh nicht. Dafür gab es getche()??



  • @Bernd25 sagte in C++ getch() eingegebenes Zeichen löschen:

    ((EingabeChar > 47) && (EingabeChar < 55)) || // Zahlen 0 - 9

    Warum schreibst du dann nicht direkt (EingabeChar >= '0') && (EingabeChar <= '9')) || oder nutzt isdigit?

    Ähnlich mit Buchstaben: es gibt https://en.cppreference.com/w/cpp/string/byte/isalpha oder du könntest einfach (ASCII vorausgesetzt) mit ((EingabeChar >= 'A') && (EingabeChar <= 'Z')) testen und hättest keine komischen Zahlen in deinem Programm.



  • Die ganze Funktion ist doch eher laienhaft.
    Warum wird "ESCAPE" zurückgeliefert? Dann muß nur mal ein User, Passwort oder sonstige Eingabe so heißen...
    Und dann noch das begrenzte char-Array (wo es dann erst zur Übergabe in einen std::string konvertiert wird, anstatt gleich diesen Typ zu benutzen)?!?



  • Vielen Dank für deine ausführliche Rückmeldung sowie für die Erklärung @SeppJ !

    Ich verwende für dieses Programm ncurses, es handelt sich insgesamt um ein Programm mit aktuell 1.500 Zeilen. Die Dokumentation zu ncurses ist naja, ich hatte schon einige Fehler, wo ich sehr lange gebraucht habe diese zu finden und zu beheben.

    Der Kunde hat mich darum gebeten oder vorgeschlagen ncurses zu verwenden. Ich habe das Projekt vor 3 Monaten begonnen, zuvor hatte ich nur Erfahrung mit PHP / Webentwicklung.

    Hätte ich früher gewusst, dass es auch andere womöglich bessere Lösungen als ncurses gibt, die besser aussehen - hätte ich diese wahrscheinlich sogar genommen, aber diese Möglichkeit habe ich nun nicht mehr - da es zu aufwendig wäre.

    Das Programm soll später in eine ISO integriert werden, sodass anschließend Archlinux installiert wird mit den angebenen Daten sowie Zusatzsoftware.



  • Danke @Swordfish für deine Antwort.

    Aktuell verwende ich wirklich nur den Befehl getch(). Eventuell erhalte ich aufgrund meines Befehls echo() welcher vor der Funktion ausgeführt wird, die Zeichen in der Konsole angezeigt.

    Mein Problem ist, dass wenn ein ungültiges Zeichen eingegeben wurde, ich dieses nicht mehr löschen kann. Daher suche ich eine andere Möglichkeit, einen String eingeben zu lassen und gleichzeitig die Möglichkeit habe auf die ESC-Taste reagieren zu können.



  • Danke für deine Antwort @Th69.

    Ich verstehe deine Anmerkung sehr gut, wenn ein User "ESCAPE" heißen soll, dann würde es zu einem Problem werden. Ich habe mein Programm auf Funktionen aufgebaut, welche alle Eingaben abfragen und diese anschließend in einen String speichern.

    Mir ist keine bessere Lösung eingefallen, als den String nach der Eingabe auszuwerten und falls "ESCAPE" "eingeben" wurde, eine Funktion zurückzuspringen. Mir fällt gerade ein, ich könnte vom User eine Eingabe von mind. 4 Zeichen erwarten, und falls ESC gedrückt wurde "ESC" zurückgeben. So hat der User auch keine Möglichkeit "ESC" zu verwenden.

    Gibt es denn eine Möglichkeit, dass ich direkt mit einem String arbeite? Ich bin davon ausgegangen, dass es die beste Möglichkeit wäre mit einem Char-Array zu arbeiten und anschließend dieses in einen String umzuwandeln.

    Ich entschuldige mich dafür, dass ich kein Experte bin und diese Programmiersprache nicht wirklich gelernt habe. Ich versuche einfach mein bestes... oft muss ich auch noch google verwenden, jedoch bin ich damit gut zurecht gekommen, jedoch finde ich leider auf meine Fragestellung dort auch keine wirkliche Antwort..



  • @wob Danke für deine Antwort! Das probiere ich definitiv aus, vielen Dank!



  • @Bernd25 Wie Dir glaube ich schon gesagt worden ist solltest Du Dich nach einer Bibliothek umsehen die Dir den Umgang mit der Konsole erleichtert. Da Du Linux erwähnt hast: https://www.linuxjournal.com/content/getting-started-ncurses



  • Danke für deine Antwort @Swordfish! Ich verwende schon ncurses, aber ich werde mir den Link von dir gleich näher ansehen. Danke für deine Mühe!



  • @Bernd25: Wenn der Anwender bei der Funktion immer etwas eingeben muß, dann könntest du bei ESC auch einfach einen Leerstring zurückgeben (anstatt eines Magic-Strings):

    return std::string();
    

    Alternative wäre z.B. eine Exception zu werfen (throw) und diese dann zu fangen (catch) - je nachdem, ob du dies als eine Programmlogik-Ausnahme ansiehst.
    Oder es ginge auch noch per zusätzlichem Parameter:

    std::string MeineFunktion(bool& ESCgedrueckt)
    {
      ESCgedrueckt = false;
    
      // ...
    }
    

    Oder ab C++11 auch ein std::tuple zurückgeben:

    std::tuple<std::string, bool> MeineFunktion()
    {
      // ...
      return std::make_tuple(AusgabeString, ESCgedrueckt);
      // bzw.
      return { AusgabeString, ESCgedrueckt };
    }
    

    (ich habe mal extra hier jetzt die deutschen Bezeichner gelassen, auch wenn ich dir anrate, alles in englisch zu erstellen!)



  • @Th69
    Was spricht gegen:

    /**
     * @brief 
     * @param s Vom Benutzer eingegebenen String
     * @return false wenn die Eingabe vom Benutzer abgebrochen wurde, sonst true
     */
    bool MeineFunktion(std::string& s)
    {
        char c;             // EingabeChar habe ich auf c (= Char) verkürzt
        int Zaehler = 0;
    
        s.clear();
        while ((c = getch()) != 13)     // 13 = Carriage return, Enter-Taste
        {
            std::cout << Zaehler;		
            if (c == 27)                // 27 = Escape, ESC-Taste
                return false;
            if (isalnum(c))
            {
                s.push_back(c);
                Zaehler++;
            }
        }
        return true;
    }
    


  • Ab C++ 17 gibt´s auch noch std::optional.



  • Genau so gut wie mein Vorschlag "per zusätzlichem Parameter" (welches nun der Rückgabewert und welches der zusätzliche Parameter ist, ist wohl Geschmackssache).



  • Ich bedanke mich für die vielen Rückmeldungen und vorallem für die Beispiele. Ihr habt mir sehr geholfen!!

    Jetzt bin ich aber dran, die Funktion entsprechend anzupassen und es auszuprobieren. Ich werde mich morgen melden, sobald ich es umgesetzt habe.

    Vielen Dank!



  • Ohne Backspace ist das allerdings ziemlich benutzerunfreundlich...
    Allerdings flackerten meine Versuche diesbezüglich (wenn BS und length()>0: Springen zum Zeilenanfang, Ausgabe von Leerzeichen, pop_back() und Ausgabe des Strings.
    Blieben noch die Cursortasten und Einfügen.
    Vermutlich wäre es einfacher, einen modalen Dialog anzuzeigen, in dem sämtliche Eingaben vorgenommen werden können.



  • Früher gab's auch mal Turbo Vision von Borland. Und da ist ein linux port: http://tvision.sourceforge.net/



  • @Quiche-Lorraine Vielen Dank für das tolle Beispiel, damit hast du mir sehr geholfen. Ich habe leider noch keine Informationen im Internet gefunden, wie ich den string anschließend auswerten kann. Jedoch denke ich, dass ich die Funktion so aufrufen muss:

    if (MeineFunktion(std::string test)) { 
    // kein ESC und Eingabe in "test"
    }
    else {
    // ESC
    }
    

    Ich nutze die Funktion aktuell so:

    bool MeineFunktion(std::string& s)
    {
        char c;             // EingabeChar habe ich auf c (= Char) verkürzt
        int Zaehler = 0;
    
        s.clear();
        while ((c = getch()) != 13)     // 13 = Carriage return, Enter-Taste
        {
            std::cout << Zaehler;		
            if (c == 27)                // 27 = Escape, ESC-Taste
                return false;
            if (isalnum(c))
            {
                s.push_back(c);
                Zaehler++;
            }
        }
        return true;
    }
    

    Da ich ncurses verwende, befindet sich der Cursor in der Mitte des Bildschirms, nach meiner Text-Ausgabe mit mvprintw. Also z. B. nach: "Hier eingeben: ". Dies ist auch so gewünscht. Gebe ich nun etwas ein und drücke ENTER verschiebt sich der Cursor ganz nach links in die Zeile, also in die erste Spalte und die Eingabe wird weiterhin erwartet.

    Gebe ich bei der Eingabe "test" ein und drücke anschließend die ESC-Taste, wird meine Eingabe einfach gelöscht und der Curser ist wieder in der Mitte vom Bildschirm wo er ursprünglich war.

    Sehr komisch irgendwie...

    EDIT: Und ich habe bei der Eingabe die Möglichkeit mit der zurück Taste, so weit zu löschen, bis ich in der ersten Spalte bin. Das ist natürlich ganz doof.... Hast du für mich einen Tipp, was ich versuchen kann? Ich danke dir!



  • @Bernd25
    Mit ncurses kenne ich mich nicht aus.

    Da ich aber eine Idee mit Control Codes hatte, habe ich folgendes ausprobiert:

    #include <string>
    #include <iostream>
    #include <cstdio>
    #include <conio.h>
    
    /**
     * @brief 
     * 
     * @todo Die Funktion nutzt die Control Codes 13 = Carriage Return, 27 = Escape und 
     * 8 = Backspace. Ich bin mit aber nicht sicher ob auf allen Terminals diese auch so
     * unterstuetzt werden, wie wir sie hier benötigen. Daher ist es wichtig diese auf 
     * dem Zielterminal zu testen. Getestete Terminals: Windows cmd.exe
     * 
     * @todo Die Funktion würde ich als Hack einstufen. Eine grafische Variante mit GUI
     * wäre mir lieber. 
     * 
     * @param s Vom Benutzer eingegebenen String
     * 
     * @return false wenn die Eingabe vom Benutzer abgebrochen wurde, sonst true
     */
    bool MeineFunktion2(std::string& s)
    {
        char c;             // EingabeChar habe ich auf c (= Char) verkürzt
    
        std::cout << "Eingabe: ";
        s.clear();
        while ((c = getch()) != 13)     // 13 = Carriage return, Enter-Taste
        {
            if (c == 27)                // 27 = Escape, ESC-Taste
            {
                std::cout << "\n";
                return false;
            }
            else if (c == 8)            // 8 = Backspace
            {
                if (!s.empty())
                {
                    s.pop_back();            
                    std::cout << c << ' ' << c;
                }
            }
            if (isalnum(c))
            {
                s.push_back(c);
                std::cout << c;
            }
            //std::cout << s.size();      // Sobald wie ein Zeichen eingegeben wurde, wollen wir auch die Länge ausgeben        
        }
        std::cout << "\n";
        return true;
    }
    
    int main(int argc, char** argv)
    {
        std::string s;
        
        if (MeineFunktion2(s))
            std::cout << "Benutzereingabe: " << s << "\n";
        else
            std::cout << "Benutzereingabe wurde mittels ESC abgebrochen\n";
        return 0;
    }
    

    Aber ich würde diesen Code als Hack bezeichnen, da ich mir nicht sicher bin ob jedes Terminal auch den ASCII Code 8 = Backspace versteht. Auf der Windows Kommandozeile cmd.exe scheint er zu funktionieren.

    Ich habe leider noch keine Informationen im Internet gefunden, wie ich den string anschließend auswerten kann.

    Doku zu std::string auf cppreference



  • @Quiche-Lorraine Vielen herzlichen Dank für deine Mühe! Das hätte ich nun wirklich nicht erwartet.

    Ich war früher schon in einigen Foren unterwegs, aber so Hilfsbereit wie hier alle sind - das habe ich nicht erwartet. Ich bin allen sehr dankbar!

    Ich werde es testen und werde anschließend berichten wie es gelaufen ist. Vielen Dank auch für den Link zur Doku von std::string auf cppreference - diese werde ich mir durchlesen. Dankeschön!