Вопросы о проверке ввода пользователя в Objective-C, число против строки - PullRequest
3 голосов
/ 17 января 2010

Почему «именно» этот код повторяется бесконечно, если вы вводите не цифру?

Первый вопрос возникает потому, что я хочу научиться хорошему защитному кодированию. Кто-нибудь знает хороший способ проверить пользовательский ввод? Мой гугл-фу подвел меня. Некоторые люди, похоже, придерживались мнения, что если я укажу %f в scanf, то я «требую» a float; Я проверил это, напечатав значение userInput. На самом деле, если я закомментирую цикл do while, с выполнением кода "не возникнет проблем". Он присваивает 0 userInput и занимается своими делами.

#import <Foundation/Foundation.h>

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    float userInput;
    float result;

    NSLog(@"3X^3 -5x^2 + 6");

    do {
        NSLog(@"What is x?");
        scanf("%f", &userInput);
        NSLog(@"userInput = %f", userInput);
    } while(userInput == 0);

    result = 3 * (userInput * userInput * userInput) - 5 * (userInput * userInput) + 6;
    NSLog(@"the result is: %f", result);

    [pool drain];
    return 0;
}

Ответы [ 2 ]

3 голосов
/ 17 января 2010

Это на самом деле не имеет ничего общего с Objective-C или какао. Проблема заключается просто в использовании стандартной функции библиотеки C scanf и обработке состояния ошибки. С справочной страницы scanf, описывающей код возврата:

Ноль означает, что, хотя вход был доступен, преобразования не были назначены; обычно это происходит из-за недопустимого входного символа, такого как буквенный символ для преобразования `% d '.

Допустимый числовой ввод может быть проанализирован с помощью scanf с помощью спецификатора %f, так что он, очевидно, работает как ожидалось. Но если вы введете нечисловой символ, scanf не сможет преобразовать его в число с плавающей точкой, а оставит текст в буфере stdin. Поскольку код не проверяет код возврата из scanf, а только проверяет, является ли userInput ненулевым, цикл никогда не завершится, поскольку userInput начинается с 0.0 и никогда не будет обновляться как scanf не будет извлекать нечисловые символы из буфера stdin. Вот почему ваш код работает в бесконечном цикле.

Если вы инициализировали userInput ненулевым значением, это решит проблему одним способом, так как нецифровый ввод приведет к сбою scanf и сработает условие while. Но лучшим решением было бы проверить код возврата scanf. Если оно равно нулю, напечатайте сообщение об ошибке и выполните fpurge(stdin), чтобы очистить недопустимый ввод перед повторным циклом, например:

int rc = scanf("%f", &userInput);
if (rc == 0)
{
    NSLog(@"Invalid input, try again.");
    fpurge(stdin);
}

Так что это простой C-подход к вводу и анализу. Суть в защитном кодировании заключается в том, что вы всегда должны проверять код возврата!

Как упоминает Крис, для реального приложения Cocoa вы бы хотели взглянуть на NSNumberFormatter и тому подобное, но тогда вы бы, вероятно, брали входные данные из виджетов, а не из файловых потоков, так что код был бы совершенно другим выше.

1 голос
/ 17 января 2010

Правильный способ проверки ввода пользователя в Какао - использовать экземпляр соответствующего подкласса NSFormatter, в данном случае что-то вроде NSNumberFormatter.

...