C ++: Справка по разнице в Cin между Linux и Windows - PullRequest
4 голосов
/ 02 мая 2010

У меня есть консольная программа Win32, которую я написал, и она отлично работает. Программа принимает ввод от пользователя и выполняет некоторые вычисления и отображает вывод - стандартные вещи. Ради интереса я пытаюсь заставить программу работать на моем компьютере Fedora, но у меня возникает проблема с очисткой cin, когда пользователь вводит что-то, что не соответствует моему типу переменной. Вот этот код:

void CParameter::setPrincipal() {
double principal = 0.0;

cout << endl << "Please enter the loan principal: ";
cin >> principal;

while(principal <= 0)
{
    if (cin.fail())
    {
          cin.clear();
          cin.ignore(INT_MAX, '\n');
    }
    else
    {
        cout << endl << "Plese enter a number greater than zero. Please try again." << endl;
        cin >> principal;
    }
}

m_Parameter = principal;

}

Этот код работает в Windows. Например, если пользователь пытается ввести тип данных char (вместо double), то программа информирует пользователя об ошибке, сбрасывает cin и предоставляет пользователю еще одну возможность ввести действительное значение.

Когда я перемещаю этот код в Fedora, он прекрасно компилируется. Когда я запускаю программу и ввожу недопустимый тип данных, цикл while никогда не прерывается, чтобы позволить пользователю изменить ввод.

Мои вопросы; как очистить cin при вводе недопустимых данных в среде Fedora? Кроме того, как мне написать этот код, чтобы он работал в обеих средах (Windows & Linux)?

Заранее спасибо за помощь!

Ответы [ 4 ]

2 голосов
/ 02 мая 2010

Я думаю, что cin.ignore устанавливает флаг сбоя на cin, что позволяет ему оставаться в самом верхнем операторе if навсегда. INT_MAX - это очень большое число - вы уверены, что cin.ignore разрешено на всех платформах?

1 голос
/ 02 мая 2010

Я думаю, что это плохая идея использовать форматированный ввод для чтения ответов пользователей. Я бы использовал getline - что-то вроде этого:

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

template <typename T>
bool Read( T & t, istream & is ) {
    string s;
    if ( ! getline( is, s ) ) {
        return false;
    }
    else {
        istringstream ss( s );
        return ss >> t;
    }
}    

int main() {
    while(1) {
        double d;
        if ( ! Read( d, cin ) ) {
            cout << "invalid\n";
        }
        else {
            cout << "You entered " << d << "\n";
        }
    }
}

, который работает в Windows - в данный момент у меня нет Linux-бокса.

1 голос
/ 02 мая 2010

Я бы переключился на использование getline для чтения ввода, а затем проанализировал бы с stringstream:

double principal = 0;
string temp;
while (principal <= 0)
{
    getline(cin, temp);
    istringstream converter(temp);
    if (!(converter>>principal) ||
        !(converter>>std::ws).eof() ||
        principal <= 0)
    {
        cout << "Try again" << endl;
        principal = 0;
    }
}
0 голосов
/ 02 мая 2010

Я согласен с Андерсом Абелем и Йоханнесом Шаубом; Я также думаю, что не может быть гарантировано, что в случае ошибки принципал останется нетронутым, поэтому вы можете рассмотреть возможность добавления principal=0.0; в начале цикла.

Кстати, для выполнения такой работы я обычно использую эту функцию шаблона:

template<typename InType> void AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString, InType & Result)
{
    do
    {
        Os<<Prompt.c_str();
        if(Is.fail())
        {
            Is.clear();
            Is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        }
        Is>>Result;
        if(Is.fail())
            Os<<FailString.c_str();
    } while(Is.fail());
}

template<typename InType> InType AcquireInput(std::ostream & Os, std::istream & Is, const std::string & Prompt, const std::string & FailString)
{
    InType temp;
    AcquireInput(Os,Is,Prompt,FailString,temp);
    return temp;
}

Первая перегрузка может быть предпочтительнее, если вы хотите избежать копирования, вторая может быть более удобной для встроенных типов. Примеры использования:

//1st overload
double aDouble;
AcquireInput(cout,cin,"Please insert an integer: ","Invalid value.\n",aDouble);

//2nd overload (more convenient, in this case and in general with POD)
double aDouble=AcquireInput(cout,cin, "Please insert an integer: ","Invalid value.\n");

В вашем случае вы можете изменить свой код следующим образом:

double principal=0.0;
const char * errMsg="Plese enter a number greater than zero. Please try again.\n";
while(principal<=0.0)
{
    principal=0.0;
    principal=AcquireInput(cout,cin,"Please enter the loan principal: ",errMsg);
    if(principal<=0.0)
        cout<<errMsg;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...