입력 예외 처리
프로그래밍을 하다보면, 사용자 입력을 받아야 할때가 생긴다. C와 C++ 에서는 표준 입력 함수인 scanf() 함수와 C++의 std::cin istream 객체가 있겠다.
이러한 것들을 이용하여 사용자 입력을 받을때 잘못된 데이터를 받는다면? 예를 들어
int num = -1;
printf("정수 입력");
scanf("%d", &num);
이와 같은 코드에서 만약 문자(%c format) 같은것을 입력한다면? 결론만 말하자면 유감스러운 결과를 보게 될 것이다. 루프안에 저 코드가 있다면 끔찍한 무한루프를 겪게 될것이며, num의 값은 -1로 고정될 것이다.
scanf() 와 sttd::cin 모두 다음과 같은 구조로 입력을 받는다.
- 입력버퍼에서 값을 읽어, 자신의 입력 포맷과 맞는지 검사한다.
- 포맷과 맞다면, 버퍼에서 값을 꺼내 입력을 받는다.
- 포맷과 맞지 않다면, 버퍼에 값을 그대로 남겨 놓는다.
- 이 작업은 버퍼가 비어있을때 까지 반복 된다. -> 루프에서 문제가 생기는건 이 과정이 반복 되기 때문이다.
즉, 버퍼를 비우지 않으면 다음 입력을 제대로 받지 못하게 된다는 것이다. 만약 값을 잘못 입력받는 예외처리를 하고 싶다면, 입력버퍼를 지워 주도록 하자.
scanf() 해결책
int num = -1;
printf("정수 입력");
rewind(stdin); // 입력버퍼 비우기
scanf("%d", &num);
std::cin 해결책
int num = -1;
// 입력에 실패하였는가?
if (std::cin.fail()) {
// 실패했다면
std::cin.ignore(INT_MAX, '\n'); // 입력하여 문자로 인식한'\n'도 무시
std::cin.clear(); // 입력 가능상태로 되돌리기
}
rewind(stdin); // 입력버퍼 비우기
std::cout << "정수를 입력하세요\n";
std::cin >> num;
std::cin은 클래스 객체이다. 함수와는 달리 내부적으로 변경된 값이 있어 해당 값을 초기화 해주는 과정이 필요하기에 rewind(stdin); 만으로는 해결이 안될것이다.
참고로 fflush(stdin); 표준이 아니라 더이상 지원이 안된다고 한다.
여담 : Visual studio 2010 이상부터는 scanf_s();를 사용하도록 권장한다. 해결책은 같다.