[완료]윤년 검사가 안되고, 실수로 문자를 입력하면 무한루프가 됩니다.

-@Naver의 이미지

나중에 우분투에서 쓸 예정인데, 편의상 윈도우10 DEV C++ 툴로 작성중입니다.
연도4자리 월2자리 일2자리 -> yyyymmdd 로 입력 받은 뒤에,
입력값이 유효한지 검사하고 싶은데요.
윤년 평년 부분이 아예 안 돌아가는 거 같습니다.ㅠㅠ
윤년. 평년 여부가 출력이 안되더라구요.ㅠㅠ
윤년 검사하는 걸 위키백과에서 찾은 대로 조건식에 넣었는데,
아예 안 돌아가는 이유를 모르겠습니다.
캡쳐화면의 입력한 연도는 위키백과에서 윤년이라고 기록된 연도를 29일로 확인해본 것입니다.

실수로 숫자가 아닌 문자를 누르면 무한루프에 빠지고(다시 입력받기를 원했는데, 무한으로...ㅠㅠ)
윤년 확인 부분은 아예 안 먹히는 거 같습니다.

제가 어느 부분을 잘못한 건지...알려주시면 정말로 고맙겠습니다.
도와주세요 ㅠㅠ

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
 
#define IDlen 9 
#define MAX 1024 // 입력받는 임시 문자열
 
void printBOF_gets(char str[], int strsize, int define_size) { 
    printf("입력한 byte(s)는 %dbyte(s)입니다.\n", strsize-1);
    printf("입력 가능한 최대 길이 %dbyte(s)보다 %dbyte(s)가 초과됐습니다.\n",
            define_size-1, strsize-define_size);
    printf("입력할 데이터를 다시 확인해보세요.\n");
}
 
int checkDate(int date) { // 입력받은 날짜의 유효성 검사-> 1이면 유효한 날짜임.
    int yyyy, mm, dd = 0;
 
    yyyy = date / 10000;
    mm = (date-(yyyy*10000)) / 100;
    dd = date % 100 ;
    printf("%d년 %d월 %d일\n", yyyy, mm, dd);
 
    if(yyyy >= 1000)
	return 0;
    if(mm == 00 || mm == 0 || mm >= 13)
	return 0;
    if(dd == 00 || dd == 0 || dd >= 32)
	return 0;
 
    if (mm == 2) { // 2월달의 윤년(29일), 평년(28일) 구분 
        if(yyyy%400 == 0) {
        	printf("윤년입니다.\n"); // 출력안됨 ㅠㅠ 
                if(dd <= 0 || dd >30)
            	    return 0;
		}
		else if((yyyy%4 == 0) && (yyyy%100 != 0)) {
        	    printf("윤년입니다.\n"); // 출력안됨 ㅠㅠ
                    if(dd <= 0 || dd >30)
            	        return 0;
		}
		else {
		    printf("평년입니다.\n"); // 출력안됨 ㅠㅠ
		    if(dd <=0 || dd > 28)
			return 0;
		}
	}
	else if (mm == 4 || mm == 6 || mm == 9 || mm == 11) { // 4, 6, 9, 11월->30일
            if (dd <= 0 || dd > 30)
		return 0;		
	}	
	else { // 1, 3, 5, 7, 8, 10, 12월->31일
	    if (dd <= 0 || dd > 31)
		return 0;
	}
    return 1;
}
 
void main() {
    int date = 0;	
    char str[MAX] = { 0, };
    char id[IDlen] = { 0, };
    int strsize = 0;
 
    while(1) {
        printf("Input the date (yyyymmdd): ");   
    	scanf("%d", &date);
 
    	sprintf(str, "%d", date);
    	strsize = strlen(str); //출력됨 
    	printf("%d\n", date); //출력됨 
    	printf("%s\n", str); // 출력됨 
    	printf("%d\n", strsize);
 
    	if(strsize == (IDlen-1)) {
	    if(checkDate(date) == 1) // 0이면 거짓
		break;
	    else
		printf("날짜를 숫자로만 정확히 입력하세요!!!\n");
	}
	if(strsize >= IDlen)
	    printBOF_gets(str, (strsize+1), IDlen);
	if(strsize < (IDlen-1))
	    printf("8자리 숫자로 정확히 입력하세요!\n");
    }
    strncpy(id, str, IDlen-1);
}

결과화면 캡쳐보다는 텍스트로 올려야 보기 편하다고 해서,
텍스트 추가합니다.

= 윈도우10의 cmd 실행 내용 =
Input the date (yyyymmdd): 20000229
20000229
20000229
8
2000년 2월 29일
날짜를 숫자로만 정확히 입력하세요!!!
Input the date (yyyymmdd): 19880229
19880229
19880229
8
1988년 2월 29일
날짜를 숫자로만 정확히 입력하세요!!!
Input the date (yyyymmdd):

= dev c++ 툴의 컴파일로그 내용 =
Compiling single file...
--------
- Filename: C:\Users\Justa\Desktop\이름없음1.c
- Compiler Name: TDM-GCC 4.9.2 32-bit Release

Processing C source file...
--------
- C Compiler: C:\Program Files (x86)\Dev-Cpp\MinGW64\bin\gcc.exe
- Command: gcc.exe "C:\Users\Justa\Desktop\이름없음1.c" -o "C:\Users\Justa\Desktop\이름없음1.exe" -m32 -I"C:\Program Files (x86)\Dev-Cpp\MinGW64\include" -I"C:\Program Files (x86)\Dev-Cpp\MinGW64\x86_64-w64-mingw32\include" -I"C:\Program Files (x86)\Dev-Cpp\MinGW64\lib\gcc\x86_64-w64-mingw32\4.9.2\include" -L"C:\Program Files (x86)\Dev-Cpp\MinGW64\x86_64-w64-mingw32\lib32" -static-libgcc -m32

Compilation results...
--------
- Errors: 0
- Warnings: 0
- Output Filename: C:\Users\Justa\Desktop\이름없음1.exe
- Output Size: 103.353515625 KiB
- Compilation Time: 0.17s

= dev c++ 툴의 컴파일러/리소스/디버그/결과검색 내용은 아무 것도 없습니다.

세벌의 이미지

실수로 숫자가 아닌 수를 누르면 무한루프에 빠지고(다시 입력받기를 원했는데, 무한으로...ㅠㅠ)
숫자 아닌 수? 무슨 뜻인지요?

일단 컴파일은 되는데 실행해보면 예상치 못한 결과가 나올 때는?
컴파일 에러 메시지 뿐 아니라 경고 메시지도 잘 읽어보면 도움될 때가 많네요.

첨부 파일 클릭해 보니 텍스트로 보여줘도 될 것을 굳이 스크린샷 찍어 놓으셨네요.
이리 하시면 도움말 주기 어려워져요. 텍스트로 할 수 있는 건 텍스트로 정보를 주세요.

-@Naver의 이미지

말씀해주신대로 10째 줄의 오타는 숫자가 아닌 수...가 아니고 숫자가 아닌 문자..로 수정했고요.
cmd 화면 캡쳐 내용은 텍스트로 추가했습니다.

조언 고맙습니다^^

초보 C언어 학습자. 실력자분들의 도움이 절실합니다.

세벌의 이미지

if(yyyy >= 1000)
	return 0;
이게 아닐까요? 윤년 여부 체크하기 전에 return 해버리니...
-@Naver의 이미지

0999년까지는 입력받지 않으려고 한 거였는데,
yyyy < 1000 으로 해야하는 것을...
완전 반대의 표시로 했네요
정말 고맙습니다. 덕분에 윤년 검사가 됩니다. ^^

초보 C언어 학습자. 실력자분들의 도움이 절실합니다.

익명 사용자의 이미지

코드가 예상대로 안 돌아갈 땐, 뭐 디버거를 붙여 보는 방법도 있습니다만,

이런 경우에는 군데군데 printf를 추가로 넣어서, 그 부분까지는 실행이 되는지 확인하고, 겸사겸사 그 시점에서의 변수 값도 확인해 보는 게 좋습니다.

예컨대 출력을 살펴보면 printf("%d년 %d월 %d일\n", yyyy, mm, dd);까지는 실행이 되는 걸 확인할 수 있지요. 문제가 있다면 그 뒤부터입니다. 어디가 문제일까요?

(네. if문 하나 실수해서 그래요. 문제는 이런 실수를 스스로 찾아낼 수 있는 능력을 갖추는 게 프로그래머로서 성장하는 데 매우 중요한 스텝이라는 겁니다.)

-@Naver의 이미지

디버거...음. 사용법을 공부해야겠네요. (프로그래밍을 공부하려면 툴 사용법도 익혀야겠네요^^)
()로 알려주신 if문은 제가 실수로 완전 반대의 조건을 달았더군요.ㅠㅠ
덕분에 if 해결되어서 윤년 확인 잘되고 있습니다.

디버거 에 대한 방법 조언과, 그리고 if 조언... 고맙습니다.^^

초보 C언어 학습자. 실력자분들의 도움이 절실합니다.

익명 사용자의 이미지

누군가 벌써 스포해버려서 김 샜군요. ㅉㅉ.

Quote:
실수로 숫자가 아닌 문자를 누르면 무한루프에 빠지고(다시 입력받기를 원했는데, 무한으로...ㅠㅠ)

이 문제는 조금 더 미묘합니다. 윤년 판별 알고리즘보다 더 어려운 문제라고 볼 수도 있어요,

"사용자가 실수를 한 경우에, 어떻게 해야 사용자를 최대한 덜 불편하게 할 수 있을까?"

대개 정답이 없는 문제이지요.

====

기술적으로 보면 간단합니다.
scanf 함수는 서식 문자열과 입력이 대응되지 않는 상황이 오면 그냥 포기해버리고 맙니다. 그리고 실제로 입력을 받는 데 성공한 argument의 수를 반환하지요.

따라서 scanf가 정상적으로 내가 의도한 만큼 입력을 받았는지 확인하려면 필히 반환값을 체크해봐야 합니다.
유감스럽게도 이런 부분을 친절하게 알려주는 C언어 교본이 그리 많지 않더군요.

만약 표준 입력에 숫자 아닌 문자가 끼어 있다면, scanf의 %d 서식 문자열은 즉시 실패합니다. 아무리 반복해 봐도 마찬가지입니다. 뭔가 잘못됐다는 걸 (반환값을 통해) 깨닫고 빠져나오지 않으면 당연히 무한 루프에 빠지죠.

문제는 그 다음입니다. "이상한" 입력을 어떻게 정상화 시킬 수 있을까요? 대충 두 가지 방법이 있을 것 같아 보이는군요.

1. 숫자가 발견될 때까지 입력된 문자를 무시한다.
2. 개행문자가 발견될 때까지 입력된 문자를 무시한 뒤, 모든 개행 문자를 무시한다.

어느 쪽이 이치에 맞는지는 상황에 따라 직접 선택하시고요.
그 구현은,

1. getc/ungetc를 이용해서 삽질을 하거나
2. scanf의 character set format specifier를 다룰 줄 알면 이게 더 편하지요. :)

====

이런 걸로 삽질하고 있다 보면, 입력을 처리(=사용자와 상호작용)하는 게 윤년 여부를 판별하는 것보다도 골치아픈 일이라는 걸 배우게 될 겁니다.

-@Naver의 이미지

제 문제에 대해 함께 확인해주는 분이 계시다는 사실만으로도 제게 큰 힘과 격려가 됩니다.
굉장히 정성스럽고, 상세하개 답을 주셨네요. 김 새지 마세요...제게는 격려가 됩니다.
음... 문자로 입력받은 경우의 무한루프... 부분이 해결이 안되어서 [완료] 로 수정을 못 한 거였는데.
삽질?이라는 결론과 과정을 얻게되어 님 덕분에 [완료]로 수정합니다.
정말 고맙습니다^^

초보 C언어 학습자. 실력자분들의 도움이 절실합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.