проверка числового ввода пользователя - PullRequest
1 голос
/ 06 февраля 2010

Я создаю простой инструмент для калькулятора CLI в качестве упражнения. Мне нужно убедиться, что n1 и n2 являются числовыми, чтобы функции работали; следовательно, я хотел бы заставить программу завершать работу при обнаружении заранее определенного нечислового значения.

Кто-нибудь может дать мне какое-то направление?

Кроме того, если кто-то может предложить какие-либо общие советы о том, как я мог бы сделать это лучше, я был бы признателен. Я только учусь с ++.

Спасибо!

Полный код приведен ниже.

#include <iostream>
#include <new>

using namespace std;

double factorial(double n) { return(n <= 1) ? 1 : n * factorial(n - 1); }

double add(double n1, double n2) { return(n1 + n2); }

double subtract(double n1, double n2) { return(n1 - n2); }

double multiply(double n1, double n2) { return(n1 * n2); }

double divide(double n1, double n2) { return(n1 / n2); }

int modulo(int n1, int n2) { return(n1 % n2); }

double power(double n1, double n2) {
    double n = n1;
    for(int i = 1 ; i < n2 ; i++) {
        n *= n1;
    }
    return(n);
}

void print_problem(double n1, double n2, char operatr) {
    cout<<n1<<flush;
    if(operatr != '!') {
        cout<<" "<<operatr<<" "<<n2<<flush;
    } else {
        cout<<operatr<<flush;
    }
    cout<<" = "<<flush;
}

int main(void) {

double* n1, * n2, * result = NULL;
char* operatr = NULL;

n1 = new (nothrow) double;
n2 = new (nothrow) double;
result = new (nothrow) double;
operatr = new (nothrow) char;

if(n1 == NULL || n2 == NULL || operatr == NULL || result == NULL) {
    cerr<<"\nMemory allocation failure.\n"<<endl;
} else {

    cout<<"\nTo use this calculator, type an expression\n\tex: 3*7 or 7! or \nThen press the return key.\nAvailable operations: (+, -, *, /, %, ^, !)\n"<<endl;

    do {    
        cout<<"calculator>> "<<flush;       
        cin>>*n1;

        cin>>*operatr;

        if(*operatr == '!') {
            print_problem(*n1, *n2, *operatr);
            cout<<factorial(*n1)<<endl;
        } else {

            cin>>*n2;

            switch(*operatr) {
                case '+':
                    print_problem(*n1, *n2, *operatr);
                    cout<<add(*n1, *n2)<<endl;
                    break;
                case '-':
                    print_problem(*n1, *n2, *operatr);
                    cout<<subtract(*n1, *n2)<<endl;
                    break;
                case '*':
                    print_problem(*n1, *n2, *operatr);
                    cout<<multiply(*n1, *n2)<<endl;
                    break;
                case '/':
                    if(*n2 > 0) {
                        print_problem(*n1, *n2, *operatr);
                        cout<<divide(*n1, *n2)<<endl;
                    } else {
                        print_problem(*n1, *n2, *operatr);
                        cout<<" cannot be computed."<<endl;
                    }
                    break;
                case '%':
                    if(*n1 >= 0 && *n2 >= 1) {
                        print_problem(*n1, *n2, *operatr);
                        cout<<modulo(*n1, *n2)<<endl;
                    } else {
                        print_problem(*n1, *n2, *operatr);
                        cout<<" cannot be computed."<<endl;
                    }
                    break;
                case '^':
                    print_problem(*n1, *n2, *operatr);
                    cout<<power(*n1, *n2)<<endl;
                    break;
                default:
                    cout<<"Invalid Operator"<<endl;
            }
        }
    } while(true);
    delete n1, n2, operatr, result;
}
return(0);
}

Ответы [ 4 ]

3 голосов
/ 06 февраля 2010

Что вы хотите сделать, это прочитать строку ввода или строку, а затем попытаться преобразовать эту строку в вашу числовую форму. Boost оборачивает это в lexical_cast, но вам это совсем не нужно. Я ответил на вопрос, похожий на ваш, дважды: здесь и здесь . Прочтите эти посты, чтобы понять, что происходит.

Вот окончательный результат:

template <typename T>
T lexical_cast(const std::string& s)
{
    std::stringstream ss(s);

    T result;
    if ((ss >> result).fail() || !(ss >> std::ws).eof())
    {
        throw std::bad_cast();
    }

    return result;
}

Используйте это так, как я обрисовал в этих постах:

int main(void)
{
    std::string s;
    std::cin >> s;

    try
    {
        int i = lexical_cast<int>(s);

        /* ... */
    }
    catch(...)
    {
        /* ... */
        // conversion failed
    }
}

Это использует исключения. Вы можете сделать это без броска, как описано в ссылках выше, перехватывая исключение bad_cast:

template <typename T>
bool lexical_cast(const std::string& s, T& t)
{
    try
    {
        t = lexical_cast<T>(s);

        return true;
    }
    catch (const std::bad_cast& e)
    {
        return false;
    }
}

int main(void)
{
    std::string s;
    std::cin >> s;

    int i;
    if (!lexical_cast(s, i))
    {
        std::cout << "Bad cast." << std::endl;
    }   
}

Это хорошо для того, чтобы сделать lexical_cast Boost без броска, но если вы реализуете это самостоятельно, нет причин тратить время на бросание и ловлю исключение. Реализуйте их в терминах друг друга, где версия для броска использует версию без броска:

// doesn't throw, only returns true or false indicating success
template <typename T>
const bool lexical_cast(const std::string& s, T& result)
{
    std::stringstream ss(s);

    return (ss >> result).fail() || !(ss >> std::ws).eof();
}

// throws 
template <typename T>
T lexical_cast(const std::string& s)
{
    T result;
    if (!lexical_cast(s, result))
    {
        throw std::bad_cast("bad lexical cast");
    }

    return result;
}

В вашем коде больше проблем: вы new все делаете! Есть ли причина для этого? Подумайте, генерирует ли какая-либо часть вашего кода исключение: теперь вы выпрыгиваете из основного и пропускаете все. Если вы выделите свои переменные в стеке, они гарантированно будут уничтожены.

1 голос
/ 06 февраля 2010

Нет необходимости в Boost или написании собственного шаблона, или принуждении себя использовать исключения против кодов ошибок. cin один делает все, что вы просите.

Вы можете проверить if ( cin ) или if ( ! cin ), чтобы определить успех или неудачу. Одна ошибка (например, буква в числовом вводе) остановит cin от принятия больше ввода. Затем вызовите cin.clear(), чтобы устранить ошибку и возобновить получение ввода, начиная с любого текста, вызвавшего ошибку. Также вы можете запросить, чтобы поток генерировал исключения при ошибках преобразования: cin.exceptions( ios::failbit ).

Итак, вы можете сделать это:

for (;;) try {
    double lhs, rhs;
    char oper;
    cin.exceptions( 0 ); // handle errors with "if ( ! cin )"
    cin >> lhs >> oper; // attempt to do "the normal thing"
    if ( ! cin ) { // something went wrong, cin is in error mode
        string command; // did user enter command instead of problem?
        cin.clear(); // tell cin it's again OK to return data,
        cin >> command; // get the command,
        if ( command == "quit" ) break; // handle it.
        else cin.setstate( ios::failbit ); // if command was invalid, 
                                           // tell cin to return to error mode
    }
    cin.exceptions( ios::failbit ); // now errors jump directly to "catch"
        // note that enabling exceptions works retroactively
        // if cin was in error mode, the above line jumps immediately to catch
    if ( oper != '!' ) cin >> rhs;
    // do stuff
} catch ( ios::failure & ) {
    cin.clear();
    cin.ignore( INT_MAX, '\n' ); // skip the rest of the line and continue
}

Это означает демонстрацию обработки ошибок с помощью iostreams. Вы можете использовать исключения или ручное тестирование, или оба.

0 голосов
/ 06 февраля 2010

Вы можете использовать сам объект потока ввода для выполнения простой проверки:

Каков наилучший способ проверки ввода в C ++ с помощью cin?

Еще один интересный подходэто может быть создание синтаксического анализатора с библиотекой Boost.Spirit , хотя это продвинутая техника , интенсивно использующая возможности метапрограммирования C ++.Если вы хотите попробовать это, проверьте Быстрый старт Примеры

0 голосов
/ 06 февраля 2010

Вероятно, многие ребята из C ++ будут ненавидеть меня за это, но даже несмотря на то, что в C ++ есть все эти новые блестящие строки, и я стараюсь оставаться со строками C ++ как можно дольше, чтобы чувствовать себя чистым, в данном случае самым простым и значительно более чистым дело в том, чтобы придерживаться доброго прошлого:

if (sscanf(input, "%d", &integer) != 1) {
 // failure to read number
}
 // happily continue and process
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...