2014-05-10 4 views
1

사용자 입력이 숫자가 아닌 경우 오류를 표시하는 코드입니다. 그러나 사용자가 영문자 (예 : 123abc)를 입력하면 오류 메시지가 두 번 반복됩니다.사용자 입력 숫자 + 문자

#include <iostream> 
using namespace std; 

int main() 
{ 
int option; 

do 
{ 
    cout <<"Type random characters (E.g : asdwefef) " ; 
    cin >> option; 

    if (cin.good()) // If numeric 
    { 

    } 
    else 
    { 
     cout << "Invalid input!" << endl; 
     cin.clear();  // Clear buffer 
     cin.ignore(INT_MAX, '\n'); 
    } 

}while (option != 0); 

return 0; 
} 

어떻게 해결할 수 있습니까? 다음을 사용하여 시도했지만 결과는 같습니다.

std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
+1

을 난 당신이 오류가 의심 이 코드에 대해 두 번 메시지 아마도''임의의 문자를 입력하십시오 ... ''와''Invalid input!''을 한 번 보았을 것입니다. 그것이 코드를 재현 할 때 얻는 것입니다. – 0x499602D2

+2

그 이유는 숫자 입력을 추출 할 때 스트림이 전체 문자 시퀀스를 구문 분석하지 않기 때문입니다. ''123 ''부분과 같이 충분합니다. 입력 "abc"의 두 번째 부분은 다음 추출을 위해 여전히 남아 있습니다. 따라서 첫 번째 반복에서'cin.good()'는'true'입니다. – 0x499602D2

+1

또한 C++ 11에서 추출에 실패한 후에'option'을'0'으로 간주 할 수 있습니다. 해당 가정을 수행하기 전에 컴파일러가이를 지원하는지 확인하십시오. 컴파일러 버전 0x499602D2

답변

2

입력 스트림은 하나씩 문자를 구문 분석합니다. 숫자 추출의 경우 스트림은 숫자가 아닌 문자를 찾을 때까지 문자를 계속 읽습니다. 이미 피연산자에 문자를 성공적으로 쓴 경우 및 숫자가 아닌 문자를 읽으려는 시도가없는 경우 std::ios_base::failbit을 설정하지 않습니다. 따라서 std::cin.good()은 첫 번째 반복에 대해 true을 반환합니다.

일반적으로 good()을 검사하는 것이 스트림 유효성 평가의 바람직한 방법이 아닙니다. 스트림에는이 작업을 수행하는 내부 불린 연산자가 있습니다. 당신이해야 할 모든 부울 식에 실제 입력 작업을 둘러싸입니다 :

if (std::cin >> option) { 
    // successful input 
} 
else { 
    // unsuccessful 
} 

을 이제 전체 입력이 숫자인지 확인하기 위해서는 문자열로 읽고 수동 구문 분석을 수행하는 가장 좋은 것입니다 스트림은 자체적으로이 작업을 수행 할 수 없으므로 (기본적으로) 또는 스트림 자체에서이 작업을 수행하도록하려면 입력이 전적으로 숫자가 아닌 것으로 판단 할 수있는 경우 오류 마스크를 설정하는 사용자 정의패싯을 생성 할 수 있습니다. 이 패싯은 스트림의 로케일에 설치됩니다.

class num_get : public std::num_get<char> 
{ 
public: 
    iter_type do_get(iter_type it, iter_type end, std::ios_base& str, 
         std::ios_base::iostate& err, long& v) const 
    { 
     auto& ctype = std::use_facet<std::ctype<char>>(str.getloc()); 
     it = std::num_get<char>::do_get(it, end, str, err, v); 

     if (it != end && !(err & std::ios_base::failbit) 
         && ctype.is(ctype.alpha, *it)) 
      err |= std::ios_base::failbit; 

     return it; 
    } 
}; 

원하는 동작을 얻을 수있는 스트림으로 로케일과 imbue()에 로케일을 설치한다 : : 원래로 변경하여 언제든지 제거 할 수 있습니다

std::locale original_locale(std::cin.getloc()); 
std::cin.imbue(std::locale(original_locale, new num_get)); 

if (std::cin >> option) { 
    // input was entirely numeric 
} 
else { 
    // input was not entirely numeric 
} 
+0

사실,'stoi'는 전체 문자열을 검사하는 대신 초기 유효한 부분을 추출합니다. – chris

+0

@chris 내 대답을 업데이트했습니다. – 0x499602D2

+0

IIRC,'boost :: lexical_cast'는 전체 문자열을 검사하지만 사람들이 좋아하지 않는 몇 가지 다른 점이 있습니다. 마지막 주석을 쓰는 동안 이것을 잊어 버렸지 만,'stoi'는이 정보를 제공 할 수있는 추가 매개 변수를 가지고 있습니다. 그래서 마지막으로 주석은 사용 된 정보를 최소한으로 사용하고, 전체 문자열을 검사하지 않으며, 반드시 확인해야합니다. 그것은 너 자신 끝까지 도달한다. – chris