Как эффективно обеспечить надежный ввод данных пользователем при использовании cin для запроса int? - PullRequest
0 голосов
/ 12 сентября 2018

Это мой первый пост здесь, и я очень новичок в программировании / C ++ (буквально через пару недель в эту кроличью нору).У меня есть этот тестовый проект в Microsoft Visual Studio 2017, и я пытаюсь выяснить, как полностью обезопасить пользовательский ввод при использовании cin.Если я спрашиваю int, и я хочу только 1 или 0, я хочу, чтобы у кого-то не было абсолютно никакого способа поместить что-то вроде 2, или n, или пробел между неправильным и правильным ответом, таким как«n 1».В настоящее время я дошел до того, что единственный способ создать нежелательный результат - это сначала ввести правильное целое число (0 или 1), а затем добавить к нему пробел и любой другой символ и этот шаблон.может сопровождаться неопределенным числом пробелов и символов после начального правильного (0 a 42 f 9130) и т. д. Помимо простого получения желаемого результата с еще более грязным кодом, мне интересно, если я пропускаю некоторыевстроенные функции, о которых я еще не слышал, которые могут сделать этот процесс намного более эффективным.Вот что я написал, чтобы добраться до этой точки:

#include <iostream>
#include <string>
#include <climits>

using namespace std;

int trap;
int wrongNumber();

int numOnly() {
    while (cin.fail())
    {
        // Using someone else's code for this while statement to figure out how to not take a char input when using an int
        // Update: Turned this into a function to be called on whenever cin has a chance to fail because users don't listen.

        cin.clear(); // clear input buffer to restore cin to a usable state
        cin.ignore(INT_MAX, '\n'); // ignore last input
        system("CLS");
        cout << "----------------------------------" << endl;
        cout << "|  You can only enter a number.  |" << endl;
        cout << "| Would you like to pick a card? |" << endl;
        cout << "|  Type 1 for yes or 0 for no!   |" << endl;
        cout << "----------------------------------" << endl;
        cin >> trap;
    }
    if (trap != 1 && trap != 0) {
        system("CLS");
        wrongNumber();
    }
    return trap;
}

int wrongNumber() {

    // At least I made this fail-safe on my own!

    while (trap != 1 && trap != 0) {
        system("CLS");
        cout << "----------------------------------" << endl;
        cout << "|    That is not a 1 or a 0!     |" << endl;
        cout << "| Would you like to pick a card? |" << endl;
        cout << "|   Type 1 for yes or 0 for no!  |" << endl;
        cout << "----------------------------------" << endl;
        cin >> trap;
    }
    if (cin.fail()) {
        system("CLS");
        numOnly();
    }
    return trap;
}

int main() {

    cout << "----------------------------------" << endl;
    cout << "| Would you like to pick a card? |" << endl;
    cout << "|   Type 1 for yes or 0 for no!  |" << endl;
    cout << "----------------------------------" << endl;
    cin >> trap;

    while (cin.fail())
    {
        numOnly();
    }

    if (trap != 1 && trap != 0) {
        wrongNumber();
    }

Ответы [ 5 ]

0 голосов
/ 12 сентября 2018

Я рекомендую не использовать целое число для хранения ответа «Да» или «Нет» и использовать вместо него строку.Таким образом, вы можете сэкономить несколько строк кода с помощью cin.fail(), cin.ignore() и cin.clear():

 int main() {

    string trap;
    cout << "----------------------------------" << endl;
    cout << "| Would you like to pick a card? |" << endl;
    cout << "|   Type 1 for yes or 0 for no!  |" << endl;
    cout << "----------------------------------" << endl;
        cin>>trap;

        while (trap != "1" && trap != "0") { //You can make this while block into a function if you prefer
            cout << "----------------------------------" << endl;
            cout << "|    That is not a 1 or a 0!     |" << endl;
            cout << "| Would you like to pick a card? |" << endl;
            cout << "|   Type 1 for yes or 0 for no!  |" << endl;
            cout << "----------------------------------" << endl;
                cin>>trap;
        }
    return 0;
}

Если вы должны использовать целое числотогда вы должны посмотреть на это и захват символов без нажатия клавиши ввода .

0 голосов
/ 12 сентября 2018

В зависимости от вашего компилятора вы можете использовать код на Си, например, getch (), и только после выполнения ваших проверок вывести его на экран.Затем вам нужно будет получить код char за char и, очевидно, собрать вашу строку.

https://www.c -lang.thiyagaraaj.com / archive / c-blog / use-of-getch-getche-and-getchar-in-c

Отказ от ответственности: не все компиляторы C ++ могут поддерживать это.Это код C, а не C ++.

0 голосов
/ 12 сентября 2018

Поскольку единственными допустимыми значениями являются 0 и 1, вам не нужно читать входные данные как int. Просто прочитайте его как строку, обрежьте все пробелы и сравните вашу строку с «0» или «1».

Конечно, вы могли бы также просто принять "y" или "n", что было бы немного более удобным для пользователя.

0 голосов
/ 12 сентября 2018

Как указано в одном из ответов, легче прочитать ввод в строку и затем оценить эту строку. Вот пример:

#include <iostream>
#include <string>

int main() {
  std::string trap;

  std::cout << "Enter 1 or 0" << std::endl;
  std::getline(std::cin, trap); // fetch user input, save into trap
  while (std::cin.fail() || (trap != "1" && trap != "0")) {
    std::cout << "That was not a 1 or 0; try again" << std::endl;
    std::getline(std::cin, trap);
  }
  return 0;
}

Этот код считывает все введенные пользователем данные, определяет, является ли это 1 или 0, и либо успешно завершается, либо запрашивает пользователя в зависимости от того, что они ввели.

Если я правильно понимаю, что-то подобное может помочь вам достичь того, что вы хотите с точки зрения защиты от несанкционированного доступа. Я рекомендую вам адаптировать свой код к чему-то похожему вместо того, чтобы иметь несколько функций, выполняющих похожие задачи.

0 голосов
/ 12 сентября 2018

Лучшее можно сделать, не полагаясь на некоторые функции ОС:

#include <iostream>
#include <limits> // std::numeric_limits
#include <cctype> // std::isspace

// function to read and discard whitespace except '\n' from a stream
std::istream& eat_whitespace(std::istream &is)
{
    char ch;

    // as long as the next character in the stream is a space and not a newline
    while (std::isspace(ch = is.peek()) && ch != '\n') 
        is.get(); // get and discard the character

    return is;
}

int read_range_strict(std::istream &is, int min, int max)
{
    int value;

    // as long as
    while (!(is >> value >> eat_whitespace) // extraction of an int fails
           || is.peek() != '\n' // or the next character in the stream is not a newline *)
           || value < min || max < value // or the value is not within the specified range
    ) {

        std::cerr << "Please enter a number between " << min << " and " << max << "!\n\n";
        is.clear(); // clear flags
        // discard everything that might be left in the stream
        is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }

    return value;
}

int main()
{
    int value;
    do {
        value = read_range_strict(std::cin, 0, 1);
    } while (true); // only for demo
}

*), которые мы пытались установить, используя eat_whitespace.Поэтому, если в потоке осталось что-то, что не является \n, мы получили мусор после числа.

...