Warum überspringt std::getline() die Eingabe nach einer formatierten Extraktion?

Lesezeit: 7 Minuten

Warum uberspringt stdgetline die Eingabe nach einer formatierten
David G

Ich habe den folgenden Code, der den Benutzer auffordert, das Alter und den Namen seiner Katze einzugeben:

#include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    std::getline(std::cin, name);
    
    if (std::cin)
    {
        std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    }
}

Was ich finde ist, dass das Alter erfolgreich gelesen wurde, aber nicht der Name. Hier ist die Ein- und Ausgabe:

Input:

"10"
"Mr. Whiskers"

Output:

"My cat is 10 years old and their name is "

Warum wurde der Name aus der Ausgabe weggelassen? Ich habe die richtige Eingabe gemacht, aber der Code ignoriert sie irgendwie. Warum passiert das?

  • Ich glaube std::cin >> name && std::cin >> std::skipws && std::getline(std::cin, state) sollte auch wie erwartet funktionieren. (Zusätzlich zu den Antworten unten).

    – jww

    11. November 2018 um 17:30 Uhr


Warum uberspringt stdgetline die Eingabe nach einer formatierten
David G

Warum passiert das?

Das hat wenig mit der Eingabe zu tun, die Sie selbst gemacht haben, sondern eher mit dem Standardverhalten std::getline() hat. Wenn Sie Ihre Eingabe für das Alter (std::cin >> age), haben Sie nicht nur die folgenden Zeichen übermittelt, sondern es wurde bei der Eingabe auch ein impliziter Zeilenumbruch an den Stream angehängt Eingeben:

"10\n"

Bei der Auswahl wird immer ein Zeilenumbruch an Ihre Eingabe angehängt Eingeben oder Zurückkehren beim Senden von einem Terminal. Es wird auch in Dateien verwendet, um zur nächsten Zeile zu gelangen. Der Zeilenumbruch wird nach der Extraktion in im Puffer belassen age bis zur nächsten E/A-Operation, wo sie entweder verworfen oder gelesen wird. Wenn der Kontrollfluss reicht std::getline()es wird sehen "\nMr. Whiskers" und der Zeilenumbruch am Anfang werden verworfen, aber die Eingabeoperation wird sofort beendet. Der Grund dafür ist, dass der Job von std::getline() soll versuchen, Zeichen zu lesen und anzuhalten, wenn es einen Zeilenumbruch findet. Der Rest Ihrer Eingabe bleibt also ungelesen im Puffer.

Lösung

cin.ignore()

Um dies zu beheben, besteht eine Möglichkeit darin, den Zeilenumbruch vorher zu überspringen std::getline(). Sie können dies telefonisch tun std::cin.ignore() nach dem ersten Eingabevorgang. Das nächste Zeichen (das Zeilenumbruchzeichen) wird verworfen, sodass es nicht mehr im Weg ist.

std::cin >> age;
std::cin.ignore();
std::getline(std::cin, name);

Ordnen Sie die Operationen zu

Wenn Sie auf ein solches Problem stoßen, liegt dies normalerweise daran, dass Sie formatierte Eingabevorgänge mit unformatierten Eingabevorgängen kombinieren. Bei einer formatierten Eingabeoperation nehmen Sie eine Eingabe und formatieren sie für einen bestimmten Typ. Das ist, was operator>>() ist für. Unformatierte Eingabeoperationen sind alles andere als das, wie z std::getline(), std::cin.read(), std::cin.get()usw. Diese Funktionen kümmern sich nicht um das Format der Eingabe und verarbeiten nur Rohtext.

Wenn Sie sich an eine einzige Formatierungsart halten, können Sie dieses lästige Problem vermeiden:

// Unformatted I/O
std::string age, name;
std::getline(std::cin, age);
std::getline(std::cin, name);

oder

// Formatted I/O
int age;
std::string first_name, last_name;
std::cin >> age >> first_name >> last_name;

Wenn Sie sich dafür entscheiden, alles als Zeichenfolgen mit den unformatierten Operationen zu lesen, können Sie sie anschließend in die entsprechenden Typen konvertieren.

  • Warum nicht einfach if (getline(std::cin, name) && getline(std::cin, state))?

    – Fred Larson

    19. August 2016 um 19:41 Uhr

  • @FredLarson Guter Punkt. Es würde jedoch nicht funktionieren, wenn die erste Extraktion eine ganze Zahl oder irgendetwas ist, das kein String ist.

    – David G

    19. August 2016 um 20:30 Uhr


  • Das ist hier natürlich nicht der Fall und es hat keinen Sinn, dasselbe auf zwei verschiedene Arten zu tun. Für eine Ganzzahl könnten Sie die Zeile in eine Zeichenfolge umwandeln und dann verwenden std::stoi(), aber dann ist es nicht so klar, dass es einen Vorteil gibt. Aber ich neige dazu, lieber nur zu verwenden std::getline() für zeilenorientierte Eingaben und kümmern uns dann um das Parsen der Zeile, wie es sinnvoll ist. Ich denke, es ist weniger fehleranfällig.

    – Fred Larson

    19. August 2016 um 20:35 Uhr

  • @FredLarson Einverstanden. Vielleicht füge ich das ein, wenn ich Zeit habe.

    – David G

    19. August 2016 um 20:39 Uhr

  • @Albin Der Grund, den Sie vielleicht verwenden möchten std::getline() Wenn Sie alle Zeichen bis zu einem bestimmten Trennzeichen erfassen und in eine Zeichenfolge eingeben möchten, ist dies standardmäßig der Zeilenumbruch. Wenn diese X Wenn die Anzahl der Zeichenfolgen nur einzelne Wörter/Token sind, kann diese Aufgabe problemlos ausgeführt werden >>. Andernfalls würden Sie die erste Zahl in eine Ganzzahl mit eingeben >>Anruf cin.ignore() in der nächsten Zeile, und führen Sie dann eine Schleife aus, wo Sie verwenden getline().

    – David G

    3. April 2020 um 18:54 Uhr


1647310208 952 Warum uberspringt stdgetline die Eingabe nach einer formatierten
Boris

Alles ist in Ordnung, wenn Sie Ihren Anfangscode wie folgt ändern:

if ((cin >> name).get() && std::getline(cin, state))

  • Danke. Dies wird auch funktionieren, weil get() verbraucht das nächste Zeichen. Es gibt auch (std::cin >> name).ignore() was ich bereits in meiner Antwort vorgeschlagen habe.

    – David G

    26. März 2014 um 12:14 Uhr

  • “..work because get()…” Ja, genau. Entschuldigung für die Antwort ohne Details.

    – Boris

    26. März 2014 um 13:14 Uhr


  • Warum nicht einfach if (getline(std::cin, name) && getline(std::cin, state))?

    – Fred Larson

    19. August 2016 um 19:41 Uhr

1647310208 352 Warum uberspringt stdgetline die Eingabe nach einer formatierten
Justin Randall

Dies geschieht aufgrund eines impliziten Zeilenvorschubs, der auch als Zeilenumbruchzeichen bezeichnet wird \n wird an alle Benutzereingaben von einem Terminal angehängt, da es dem Stream mitteilt, eine neue Zeile zu beginnen. Sie können dies sicher berücksichtigen, indem Sie verwenden std::getline wenn nach mehreren Zeilen mit Benutzereingaben gesucht wird. Das Standardverhalten von std::getline liest alles bis einschließlich des Newline-Zeichens \n aus dem Input-Stream-Objekt, das ist std::cin in diesem Fall.

#include <iostream>
#include <string>

int main()
{
    std::string name;
    std::string state;

    if (std::getline(std::cin, name) && std::getline(std::cin, state))
    {
        std::cout << "Your name is " << name << " and you live in " << state;
    }
    return 0;
}
Input:

"John"
"New Hampshire"

Output:

"Your name is John and you live in New Hampshire"

Warum uberspringt stdgetline die Eingabe nach einer formatierten
Miraz

Da jeder oben das Problem für die Eingabe beantwortet hat 10\nMr Whisker\n, möchte ich einen anderen Ansatz beantworten. Alle oben veröffentlichten Lösungscodes für den Puffer sind wie 10\nMr Whisker\n. aber was ist, wenn wir nicht wissen, wie sich der Benutzer bei der Eingabe verhalten wird? der Benutzer könnte tippen 10\n\nMr. Whisker\n oder 10 \n\n Mr. whisker\n aus Versehen. In diesem Fall funktionieren die obigen Codes möglicherweise nicht. Daher verwende ich die folgende Funktion, um eine Zeichenfolgeneingabe zu erhalten, um das Problem zu beheben.

string StringInput()  //returns null-terminated string
{
    string input;
    getline(cin, input);
    while(input.length()==0)//keep taking input until valid string is taken
    {
        getline(cin, input);
    }
    return input.c_str();
}

Die Antwort wäre also:

#include <iostream>
#include <string>

int main()
{
    int age;
    std::string name;

    std::cin >> age;
    name = StringInput();
    
    std::cout << "My cat is " << age << " years old and their name is " << name << std::endl;
    
}

Extra:

Wenn Benutzereingaben a \n10\n \nmr. whiskey; Um zu prüfen, ob int ob die Eingabe gültig ist oder nicht, kann mit dieser Funktion überprüft werden int Eingabe (Programm wird undefiniertes Verhalten haben, wenn char wird statt als Eingabe angegeben int):


//instead of "std::cin>>age;" use "get_untill_int(&age);" in main function.
void get_Untill_Int(int* pInput)//keep taking input untill input is `int or float`
{
    cin>> *pInput;
    /*-----------check input validation----------------*/
    while (!cin) 
    {
        cin.clear();
        cin.ignore(100, '\n');
        cout<<"Invalid Input Type.\nEnter again: ";
        cin >>*pInput;
    }
    /*-----------checked input validation-------------*/
}

Warum uberspringt stdgetline die Eingabe nach einer formatierten
Armin Montigny

Ich frage mich wirklich. C++ hat eine dedizierte Funktion zum Auffressen aller verbleibenden oder was auch immer für Leerzeichen. Es wird genannt std::ws. Und dann können Sie einfach verwenden

std::getline(std::cin >> std::ws, name);

Das sollte der idomatische Ansatz sein. Für jeden Übergang zwischen formatierter und unformatierter Eingabe, die verwendet werden sollte.

Wenn wir nicht über Leerzeichen sprechen, sondern beispielsweise Buchstaben eingeben, wo eine Zahl erwartet wird, dann sollten wir der CPP-Referenz folgen und sie verwenden

.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); das falsche Zeug zu beseitigen.

Bitte lesen Sie Hier

1003660cookie-checkWarum überspringt std::getline() die Eingabe nach einer formatierten Extraktion?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy