2011-10-16 9 views
15

Próbuję uzyskać kilka parametrów wejściowych użytkownika z konsoli, dwa ciągi, dwa numery int i podwójne. Odpowiedni kod Próbuję użyć to:C++ getline() nie czeka na dane wejściowe z konsoli, gdy jest wywoływana wiele razy

#include <string> 
#include <iostream> 
using namespace std; 

// ... 

string inputString; 
unsigned int inputUInt; 
double inputDouble; 

// ... 

cout << "Title: "; 
getline(cin, inputString); 
tempDVD.setTitle(inputString); 

cout << "Category: "; 
getline(cin, inputString); 
tempDVD.setCategory(inputString); 

cout << "Duration (minutes): "; 
cin >> inputUInt; 
tempDVD.setDuration(inputUInt); 

cout << "Year: "; 
cin >> inputUInt; 
tempDVD.setYear(inputUInt); 

cout << "Price: $"; 
cin >> inputDouble; 
tempDVD.setPrice(inputDouble); 

Jednak po uruchomieniu programu, zamiast czekać na pierwszy inputString należy wprowadzić kod nie zatrzymać aż do drugiej getline() wezwanie . Zatem wyjście konsola wygląda następująco:

Tytuł Kategoria:

pomocą kursora pojawiającego się po kategorii. Jeśli teraz wprowadzę, program przeskoczy do przodu na rok, nie pozwalając mi wprowadzić więcej niż jednego ciągu znaków. Co tu się dzieje?

+0

nie można odtworzyć; proszę zamieścić prawdziwy, pełny kod. Założę się, że twój problem leży gdzie indziej. Nie mieszaj również sformatowanych danych wejściowych z 'getline()'. –

+0

@KerrekSB: Zgadzam się, że ich mieszanie jest symptomem złego stylu, ale jaki jest obiektywny powód, dla którego ich nie mieszasz? Myślę, że to naprawdę ważne, żeby je wymieszać. – sehe

+0

@sehe: Problem polega na tym, że sformatowane wyodrębnianie nie pochłania nowych linii, więc bardzo łatwo uzyskać nieoczekiwane wyniki, gdy wykonasz 'getline()' po tym, jak już przetworzyłeś całą poprzednią linię. Nie mówię, że to niemożliwe, ale często sprawia, że ​​logika jest trudna do odczytania i utrzymania. –

Odpowiedz

16

Problem polega jesteś mieszanie wywołań getline() z wykorzystaniem operatora >>.

Pamiętaj, że operator >> zignorował wiodącą białą przestrzeń, więc będzie poprawnie kontynuował przekraczanie linii. Ale przestaje czytać po tym, jak dane wejściowe zostały pomyślnie pobrane, a zatem nie połkną końcowych znaków "\ n". Tak więc, jeśli użyjesz getline() po >>, zwykle dostajesz coś niewłaściwego, chyba że jesteś ostrożny (aby najpierw usunąć znak "\ n", który nie został odczytany).

Sztuką jest nie używać obu rodzajów danych wejściowych. Wybierz odpowiedni i trzymaj się go.

Jeśli są to wszystkie liczby (lub obiekty, które dobrze grają z operatorem >>), po prostu użyj operatora >> (ciąg znaków jest jedynym podstawowym typem, który nie jest symetryczny z wejściem/wyjściem (tzn. Nie ładnie się odtwarza)) .

Jeśli dane wejściowe zawierają ciągi lub kombinację elementów, które będą wymagały getline(), należy użyć tylko getline() i przeanalizować liczbę z ciągu.

std::getline(std::cin, line); 
std::stringstream linestream(line); 

int value; 
linestream >> value; 

// Or if you have boost: 
std::getline(std::cin, line); 
int value = boost::lexical_cast<int>(line); 
7

Musisz opróżnić bufor wejściowy. Można to zrobić za pomocą cin.clear(); cin.sync();.

3

Zastosowanie cin.clear() jak wspomniano i użyć odpowiedniej obsługi błędów:

cin.clear(); 
    cin.sync(); 

    cout << "Title: "; 
    if (!getline(cin, inputString)) exit 255; 
    tempDVD.setTitle(inputString); 

    cout << "Category: "; 
    if (!getline(cin, inputString)) exit 255; 
    tempDVD.setCategory(inputString); 

    cout << "Duration (minutes): "; 
    if (!(cin >> inputUInt)) exit 255; 
    tempDVD.setDuration(inputUInt); 

    cout << "Year: "; 
    if (!(cin >> inputUInt)) exit 255; 
    tempDVD.setYear(inputUInt); 

    cout << "Price: $"; 
    if (!(cin >> inputDouble)) exit 255; 
    tempDVD.setPrice(inputDouble); 
+1

Dzięki. Kod działa tylko wtedy, gdy użyte są oba cin.clear(), a następnie cin.sync(). – user754852

+0

Najprostszym sposobem na połączenie operatora >> z metodą getline() jest wpisanie cin.ignore() przed dowolnym wywołaniem, aby bufor wejściowy został usunięty z pozostałych znaków nowego wiersza z poprzedniego wejścia. –

0

Mieszanie getline() ze strumieniami wejściowymi jest generalnie kiepską rzeczą. Teoretycznie możliwe jest ręczne radzenie sobie z brudnymi buforami pozostałymi za pomocą strumieni, ale jest to niepotrzebny ból, którego zdecydowanie należy unikać.

Lepiej korzystaj z biblioteki konsoli, aby pobrać dane wejściowe, w ten sposób brudna praca może zostać dla ciebie wyodrębniona.

Spójrz na TinyCon. Możesz po prostu użyć metody statycznej tinyConsole :: getLine() w miejsce twoich połączeń getline i stream i możesz jej używać tyle razy, ile chcesz.

Można znaleźć informacje tutaj: https://sourceforge.net/projects/tinycon/

4

Można użyć

cin.ignore(); 

lub jako @kernald wspomniany korzystanie

cin.clear(); 
cin.sync(); 

przed użyciem getline()

Powiązane problemy