2012-01-16 2 views
1

Если я хочу проверить свои входные данные, всегда целые числа, как я могу это сделать? Я имею в виду, что я хочу поместить ввод в вектор, но я хочу убедиться, что значения являются целыми. Я знаю, когда я печатаю, например «А», он выпрыгнет из цикла while. Но я бы хотел, чтобы это было более элегантно, как я могу это сделать. Любое предложение? ps: Я не хочу использовать char для тестирования и преобразования.Как проверить входные данные всегда целочисленного типа?

Вот простой код. Большое спасибо.

#include <iostream> 
#include <vector> 
using namespace std; 

int main() 
{ 
vector<int> ivec; 
int num; 

cout << "Please enter some integers for input or (exit) to exit" << endl; 

while(cin >> num) 
{ 
    ivec.push_back(num); 
} 
..... 
+1

[Это как вы это делаете] (http://stackoverflow.com/questions/1283302/user-input-of-integers-error-handling). –

ответ

0

This actually takes a little more effort than you may expect. Мой совет должен был бы обернуть это в шаблоне вспомогательную функцию, которая принимает начальное и сообщение об ошибке, чтобы показать (хотя это приведет к дополнительной копии).

Учитывая, что примеры кода приветствуются, вот три общих способа сделать это. Прежде всего, мы могли бы читать, если у нас есть действительные целые числа: это более простой способ. (Обязательно включает в себя, как предполагается, присутствует.)

int main() { 
    std::vector<int> values; 
    int i; 
    while((std::cout << "Enter a number: ") && (std::cin >> i)) 
     values.push_back(i); 
    // We've finished reading all values we could, let's work on them. 
} 

Этот путь короткий и простой, и позволит пользователю продолжить ввод «выход», или на самом деле что-нибудь еще, что это не число. Преимущество состоит в том, что вы можете перенаправить файл и не придется «выходить» в конце файла (EOF правильно завершит цикл). Однако это означает, что пользователь, допустивший ошибку, приводит к невозможности ввода каких-либо дополнительных данных.

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

// You could pass in output streams for prompts and errors, too. 
template<typename T> 
T read(std::istream& is, std::string const& prompt, std::string const& error) { 
    T t; 
    while ((std::cout << prompt) && !(is >> t)) { 
     if (is.eof()) 
      throw std::runtime_error("End of stream."); 
     std::cerr << error; 
     is.clear(); 
     is.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
    } 
    return t; 
} 

int main() { 
    std::vector<int> values; 
    int i = 1; 
    // This way, an input of 0 will terminate the loop. I've deviated from 
    // `exit' terminating the loop -- sentinel values like that aren't very 
    // flexible, so I wouldn't advise using it. 
    try { 
     while (i = read<int>(std::cin, "Please enter an integer: \n", "Error!\n")) 
      values.push_back(i); 
    } 
    catch (std::exception& e) { 
     // Have fun figuring out if this is end of input or something utterly unrelated. 
    } 
} 

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

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

template<typename OUT, typename IN> 
OUT lexical_cast(IN const& in) { 
    std::stringstream ss; 
    OUT o; 
    ss << in; 
    ss >> o; 
    // You may want more thorough error checking code 
    if (!ss) 
     throw std::runtime_error("Bad cast"); 
    return o; 
} 

Это будет переводить с одного типа на другой на основе текстуально, а не бинарный, представление. Теперь вы можете сделать

int main() { 
    std::vector<int> values; 
    std::string input; 
    while ((std::cout << "Enter an integer: ") && (std::cin >> input)) { 
     if (input == "exit") 
      break; 
     try { 
      values.push_back(lexical_cast<int>(input)); 
     } 
     catch (std::runtime_error& e) { 
      // Tell about the error, etc. 
     } 
    } 
} 

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

+0

Хотя ссылка имеет отношение к общему случаю ввода/вывода, я думаю, что более специализированный ответ (или, возможно, выдержка, посвященная конкретному вопросу здесь) поможет много. –

+0

@ MatthieuM. - Лучше? :) –

+0

Определенно! Два замечания (хотя я против ...) Во втором решении: чтобы избежать проблемы «исключение в регулярном потоке», вы можете передать вектор в (и добавить в функцию), а затем вернуть логическое значение, указывающее, следует ли продолжать или не. Для реализации 'lexical_cast' проверка'! Ss' недостаточна. Вы также хотите проверить, что вы полностью опорожнили поток (без остатка). В противном случае я могу ввести '1; 2', и вы будете принимать вход (как' 1') и тихо отбросить часть '; 2'. –

0

Существует встроенная функция atoi, которая преобразует массив символов в целое число.Однако существуют три крайние случаи:

  • , если преобразование не удается, возвращается 0, так что вы должны различать два случая, когда 0 возвращается

  • если целое значение слишком велико INT_MAX возвращается

  • если целое значение слишком мало INT_MIN возвращается

Именно поэтому я предпочитаю, чтобы изменить его немного и час andle эти случаи:

bool safe_atoi(const char* str, int *value) { 
    *value = atoi(str); 
    if (*value == 0) { 
    return str[0] == '0' && str[1] == 0; 
    } 

    if (*value != INT_MAX && *value != INT_MIN) { 
    return true; 
    } 

    if (*value == INT_MAX) { 
    char buf[12]; 
    sprintf(buf, "%d", INT_MAX); 
    return strncmp(buf, str, strlen(buf)) == 0; 
    } 

    if (*value == INT_MIN) { 
    char buf[12]; 
    sprintf(buf, "%d", INT_MIN); 
    return strncmp(buf, str, strlen(buf)) == 0; 
    } 

    return false; 
} 

, а затем с помощью этой функции вы можете добиться того, что вы хотите:

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

int main() 
{ 
    vector<int> ivec; 
    int num; 

    cout << "Please enter some integers for input or (exit) to exit" << endl; 

    string input; 

    while(cin >> input) 
    { 
    if (safe_atoi(input.c_str(), &num)) { 
     ivec.push_back(num); 
    } else { 
     // handle that case 
    } 
    } 
+0

Доброта ... 'boost :: lexical_cast ' мальчик! –

+0

Согласен ... однако использование boost не всегда является опцией. Случилось со мной работать в компаниях, где они не хотят использовать его по некоторым причинам. –

+0

, тогда вам повезло, чем вы можете избавиться от кода, пока вы сохраняете либеральную лицензию. Одной из причин не использовать 'lexical_cast', однако, может быть производительность.Он использует «струнный поток» под капотом и, безусловно, более быстрые способы преобразования целых чисел. Тем не менее, я бы посоветовал создать «более безопасную» альтернативу вашей C-функции: в C++ рекомендуется использовать ограниченные буферы (передавать конечный указатель или размер ... или лучше использовать непосредственно ссылку на «string» здесь), и вы, вероятно, вернете «int» и используете исключения для ошибок ... или, по крайней мере, передаете по ссылке. –

-1

Я думаю, что в C++ единственный вариант является для чтения строки.

Вы возвращаетесь обратно к использованию сканера C, который возвращает количество записей, успешно прочитанных и будет возвращать 0, если вы попытаетесь прочитать целое число, но введены символы.

+0

-1, чтение целых чисел (и, фактически, все, что перегружает 'operator >>' соответствующим образом) возможно с потоками. –

0

Вы также можете попробовать регулярные выражения. В C++ 11:

#include <iostream> 
#include <regex> 
#include <string> 

using namespace std; 

int main() 
{ 
    string input; 
    regex integer("(\\+|-)?[[:digit:]]+"); 
    //As long as the input is correct ask for another number 
    while(true) 
    { 
     cout<<"Give me an integer!"<<endl; 
     cin>>input; 
     //Exit when the user inputs q 
     if(input=="q") 
      break; 
     if(regex_match(input,integer)) 
      cout<<"integer"<<endl; 
     else 
     { 
      cout<<"Invalid input"<<endl; 
     } 
    } 
} 

Этот пример можно найти here, full credit to the Author. Если ваш компилятор еще не поддерживает регулярные выражения C++ 11, ваша следующая лучшая альтернатива - boost.regex. Это довольно сложное решение, однако оно доступно для других видов входов.

+0

это нормально, пока какой-то странный не войдет в '2 ** 32', который не поместится в ваше целое число, а затем весь ад сломается. –

+0

@MatthieuM. Вы потеряли меня там, вы имеете в виду целую цифру длиной 32 цифры? Вы всегда можете проверить длину ввода, если это соображение. И вообще, это все равно целое число, и оно будет вписываться в int на 64-битной платформе :). –

+0

Я имею в виду целое число, которое не вписывается в 'int' (подсказка: даже на 64-битной платформе,' int' - 4 байта для большинства компиляторов, чтобы не нарушать устаревший код). Конечно, вы можете выполнить проверку длины, и это легко, что на самом деле не обрабатывает все случаи. '2 147 483 648' - это не нормально, но' 2 147 483 647', хотя они имеют одинаковую длину. поэтому, как только вы нажмете 10 цифр, вам нужно подтвердить цифру цифрой. О, и '-2 147 483 648' IS fine (это не симметрично) ... –

1

Это зависит от того, хотите ли вы изобрести колесо (DIY), или если вы предпочитаете стоять на плечах гигантов.

В последнем случае я могу только посоветовать boost::lexical_cast<T>.

#include <iostream> 
#include <string> 
#include <vector> 

#include <boost/lexical_cast.hpp> 

int main() { 
    std::string buffer; 
    std::vector<int> vec; 

    std::cout << "Please enter an integer:" << std::endl; 
    while (true) { 
    std::cin >> buffer; 

    try { 
     vec.push_back(boost::lexical_cast<int>(buffer)); 
     break; // exit the loop 
    } catch(boost::bad_lexical_cast const&) {} 

    std::cout << "Sorry, '" 
       << buffer 
       << "' could not be parsed as an integer, please try again." 
       << std::endl; 
    } 

    return vec.size() == 1; 
} 
Смежные вопросы