2015-03-24 2 views
1

Мой профессор C++ непреклонен в том, что при проверке отказа ввода необходимо использовать отдельные петли while() для каждого отдельного входа. Он указал, что следующий метод проверки более одного входа собрались в одном cin заявление не будет работать:Ошибка ввода C++: несколько входов в одном заявлении

while (!good){ 
    cout << "Enter the length and the width of the rectangle: "; 
    cin >> length >> width; 
    if(!cin){ 
     cout << "Bad input, try again" << endl; 
     cin.clear(); 
     cin.ignore(200, '\n');} 
    else { 
     good = true;}} 

Его предложенный метод:

bool good = false; 
while (!good){ 
    cout << "Enter the length rectangle: "; 
    cin >> length; 
    if(!cin){ 
     cout << "Bad input, try again" << endl; 
     cin.clear(); 
     cin.ignore(200, '\n');} 
    else { 
     good = true;}} 
while (good){ 
    cout << "Enter the width rectangle: "; 
    cin >> width; 
    if(!cin){ 
     cout << "Bad input, try again" << endl; 
     cin.clear(); 
     cin.ignore(200, '\n');} 
    else { 
     good = false;}} 

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

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

+0

Ваш преподаватель должен узнать об этом новом синтаксисе (всего около 40 лет) называется Do-то время. –

+0

Сближение с греческим псевдонимом: обратите больше внимания, ни одна из версий кода не проверяет флаг 'eof'. –

+0

Вы можете найти [C++ FAQ: как я могу получить std :: cin для пропуска недопустимых символов ввода?] (Https://isocpp.org/wiki/faq/input-output#istream-and-ignore) полезно (см. второй пример кода.) –

ответ

3

Оба являются «правильными» в том смысле, что они восстанавливаются с отказавших входов и дают пользователю еще один шанс.

Оба они полезны только для интерактивного ввода. Вы упоминаете чтение из файла - восстановление действительно невозможно. Игнорирование искаженного поля просто вызовет потребление следующего поля, а интерпретируется иначе, чем его местоположение в файле указывает. Лучший способ действий при чтении из файла - выводить как можно более подробное объяснение (именно там, где в файле произошла ошибка, например номера строк и столбцов, какие данные ожидались, а также о встречающихся данных были недопустимыми) ,

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

Написание цикла во второй раз невероятно бессмысленно. Вместо этого вы должны написать вспомогательную функцию, что-то вроде:

template<typename T> 
T prompt_for_value(const char* const prompt) 
{ 
    T result; 
    while (true) { 
     std::cout << prompt << std::flush; 
     if (std::cin >> result) return result; 
     std::cout << "Bad input, try again" << std::endl; 
    } 
} 

double width = prompt_for_value<double>("Enter the width in meters: "); 
double height = prompt_for_value<double>("Enter the height: "); 

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

Благодарит поддержка лямбда C++ 11, то теперь очень легко добавить проверку параметров на вспомогательную функцию:

T prompt_for_value(const char* const prompt, std::function<bool(T)> validation = {}) 
{ 
    T result; 
    while (true) { 
     std::cout << prompt << std::flush; 
     if (std::cin >> result) { 
      if (validation && !validation(result)) { 
       std::cout << "Input out of range, try again" << std::endl; 
       continue; 
      } 
      return result; 
     } 
     std::cout << "Bad input, try again" << std::endl; 
    } 
} 

double width = prompt_for_value<double>("Enter the width in meters: ", 
             [](int w) { return w >= 0; }); 
double height = prompt_for_value<double>("Enter the height: ", 
        [&width](int h) { return (h >= 0) && (h <= 10000.0/width); })); 
+0

Это вызовет бесконечный цикл, если будет прочитано из файла. –

+0

@remyabel: Так будет оригинал. И я ясно сказал, что этот метод повторной попытки полезен только для интерактивного ввода. И я жестко закодировал 'cin' и' cout', чтобы уменьшить вероятность ошибочного использования его для ввода файла. Вы ПРОЧИТАЛИ ответ? –

0

То, что вы указали в качестве предлагаемого способа вашего профессора очень плохой форме, как указано из-за к нарушению don't-repeat-yourself (DRY) principle в коде. Если вы хотите сделать одно и то же много раз, вам следует либо использовать цикл, либо функцию, чтобы избежать написания дублирующей логики.

Ваш метод включает проверку «все или ничего» и, следовательно, может потребовать, чтобы пользователь повторялся. Юзабилити этого следует решать в каждом конкретном случае.

Оба метода не могут обнаружить посторонние символы в конце строки ввода. Такой ввод может быть разумно интерпретирован как ошибочный - независимо от того, работает ли он с последующим вводом запроса.

Однако, как вы продемонстрировали, подход «все или ничего» фактически работает. Учитывая это, вот альтернативная реализация «все или ничего», основанная на автоматической проверке на основе исключений, на которую способен iostream library.

cin.exceptions(~std::ios::goodbit); 

    int length=0, width=0; 

    for (bool ok=false; !ok;) 
     { 
     try 
     { 

     cout << "Enter the length and the width of the rectangle: "; 
     cin >> std::skipws >> length >> width; 

     std::string s; 
     std::getline(cin, s); 
     s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end()); 
     if (!s.empty()) throw std::runtime_error("too much input"); 

     ok = true; 
     } 
     catch(std::ios::failure const & e) 
     { 
     cout<<"\n"<<"bad input ["<<e.what()<<"], try again...\n"; 
     cin.clear(); 
     cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
     } 
     catch(std::runtime_error const & e) 
     { 
     cout<<"\n"<<e.what()<<", try again...\n"; 
     } 
     } 

    cout<<"\n"<<"length="<<length<<", width="<<width<<"\n"; 

Выход:

Enter the length and the width of the rectangle: x 

bad input [basic_ios::clear], try again... 
Enter the length and the width of the rectangle: 1 x 

bad input [basic_ios::clear], try again... 
Enter the length and the width of the rectangle: 1 2 x 

too much input, try again... 
Enter the length and the width of the rectangle: 1 2 3 

too much input, try again... 
Enter the length and the width of the rectangle: 4 5 

length=4, width=5 
+0

Проверка завершающих символов - достойная идея, но обработка исключений добавляет сложность без каких-либо преимуществ. Исключения должны использоваться для обеспечения централизованной обработки ошибок с нескольких уровней в стеке вызовов, а не для локальной повторной попытки. –

+0

Использование библиотеки iostreams является сложным, и есть, по крайней мере, вероятность того, что включение исключений упрощает работу, особенно если вы хотите выполнять несколько операций ввода-вывода. Использование исключений дает мне больше уверенности в том, что если что-то пойдет не так, я буду знать об этом. Я не вижу никакого вреда в использовании исключений здесь вместо ручной проверки - конечно, скорость исполнения не вызывает беспокойства. У моей реализации больше строк кода, но в основном это поддержка дополнительной проверки состояния ошибок. – nobar

+0

Мне нравится указывать режим исключения для iostreams, особенно потому, что для ввода-вывода на основе файлов можно использовать метод «ничего или ничего» * ​​намного проще/проще, минуя ручную проверку ошибок. Я предполагаю, что альтернатива сравнимой сложности будет состоять в том, чтобы игнорировать все ошибки до тех пор, пока вы не завершили всю последовательность ввода-вывода и * только тогда * спросите библиотеку, действительно ли она работает. Может быть, это разумный подход, но для меня это кажется неэлегантным и склонным к ошибкам. – nobar

Смежные вопросы