鱼C论坛

 找回密码
 立即注册
查看: 4160|回复: 9

[技术交流] 输入年月日判断第几天里关于输入错误信息在次输入的问题!

[复制链接]
发表于 2012-3-27 11:31:36 | 显示全部楼层 |阅读模式
5鱼币
本帖最后由 莫名其妙 于 2012-3-28 09:46 编辑

  1. /**************************
  2. 题目:输入某年某月某日,判断这一天是这一年的第几天?
  3. 变量:y=year;m=month;d=day; t= 天数;
  4. ******************************/
  5. #include <stdio.h>
  6. void main()
  7. {
  8. int y,m,d,t;
  9. printf("输入年\n");
  10. s1: scanf("%d",&y);
  11. if(y<=0)                                      //利用scanf返回值;
  12. { printf("error\n重新输入年\n");
  13. getchar();
  14. goto s1;
  15. }

  16. printf("输入月\n");
  17. s2: scanf("%d",&m);
  18. if(m>12||m<=0)
  19. { printf("error\n重新输入月\n");
  20. getchar();
  21. goto s2;
  22. }

  23. printf("输入日\n");
  24. s3: scanf("%d",&d);
  25. if(d>31||d<=0)
  26. { printf("error\n重新输入日\n");
  27. getchar();
  28. goto s3;
  29. }
  30. if(m==2)
  31. { if(d>29)
  32. { printf("error\n重新输入日\n");
  33. getchar();
  34. goto s3;
  35. }
  36. }

  37. switch (m)
  38. {
  39. case 1:   t=d;break;
  40. case 2:   t=d+31;break;
  41. case 3:   t=d+31+28;break;
  42. case 4:   t=d+31+28+31;break;
  43. case 5:   t=d+31+28+31+30;break;
  44. case 6:   t=d+31+28+31+30+31;break;
  45. case 7:   t=d+31+28+31+30+31+30;break;
  46. case 8:   t=d+31+28+31+30+31+30+31;break;
  47. case 9:   t=d+31+28+31+30+31+30+31+31;break;
  48. case 10: t=d+31+28+31+30+31+30+31+31+30;break;
  49. case 11: t=d+31+28+31+30+31+30+31+31+30+31;break;
  50. case 12: t=d+31+28+31+30+31+30+31+31+30+31+30;break;
  51. }  /*        1  2  3  4  5  6  7  8  9  10  11 */

  52. if(y%4==0)   //闰年判断 是闰年 天数+1
  53. {
  54. if((y%100==0)&&(y%400==0))
  55. {
  56. if(m>2)
  57. {
  58. t=t+1;
  59. }
  60. }
  61. }
  62. printf("%d",t);
  63. }
复制代码
在scanf 函数里利用返回值判断是否在次输入 这里只能使用goto 语句进行重复输入么? 还有没有别的语句进行的?
还有就是getchar() 这里如果输入多个字母,回车以后就会有多条提示语句 有没有办法调整成输入一串字母也只提示一个错误信息的
限定输入字符个数是不是需要用数组限制? 比如说输入年份□ □ □ □ 需要设定a[4] 就能限制输入4个数字?

最佳答案

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-3-27 11:31:37 | 显示全部楼层
  1. #include <stdio.h>
  2. int get_year( char* msg );
  3. int get_month( char* msg );
  4. int main(){
  5.         printf("input year:");
  6.         printf("year = %d\n", get_year("Error year!"));
  7.         printf("input month:");
  8.         printf("month = %d\n", get_month("Error month!"));
  9. }

  10. int get_year( char* msg ){
  11.         int result;
  12.         while( scanf("%d", &result)!=1 || result<=0  ){               
  13.                 clearerr(stdin);
  14.                 fflush(stdin);
  15.                 printf("%s\n", msg);
  16.         }
  17.         return result;
  18. }

  19. int get_month( char* msg ) {
  20.         int result;
  21.         while( scanf("%d", &result)!=1 || !(result>=1 && result<=12)  ){               
  22.                 clearerr(stdin);
  23.                 fflush(stdin);
  24.                 printf("%s\n", msg);
  25.         }
  26.         return result;
  27. }
复制代码
示意代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2012-3-27 11:46:28 | 显示全部楼层
可以用循环判断  do  while
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2012-3-27 11:48:32 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2012-3-27 12:55:18 | 显示全部楼层
你可以再加个变量进行逻辑判断循环处用这个变量
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2012-3-27 19:02:46 | 显示全部楼层
/**************************

题目:输入某年某月某日,判断这一天是这一年的第几天?

变量:y=year;m=month;d=day; t= 天数;

******************************/

#include <stdio.h>

int main()

{

int y,m,d,t;
  do
{
        printf("输入年\n");

        scanf("%d",&y);
}while (y<=0);
do
{
        printf("输入月\n");

        scanf("%d",&m);

}while(m>12||m<=0);
do
{
        printf("输入日\n");

        scanf("%d",&d);
    if(m==2)

{if(d>29)

printf("error\n重新输入日\n");
scanf("%d",&d);}
}while (d>31||d<0);
        switch (m)

{

case 1:   t=d;break;

case 2:   t=d+31;break;

case 3:   t=d+31+28;break;

case 4:   t=d+31+28+31;break;

case 5:   t=d+31+28+31+30;break;

case 6:   t=d+31+28+31+30+31;break;

case 7:   t=d+31+28+31+30+31+30;break;

case 8:   t=d+31+28+31+30+31+30+31;break;

case 9:   t=d+31+28+31+30+31+30+31+31;break;

case 10: t=d+31+28+31+30+31+30+31+31+30;break;

case 11: t=d+31+28+31+30+31+30+31+31+30+31;break;

case 12: t=d+31+28+31+30+31+30+31+31+30+31+30;break;

}  /*        1  2  3  4  5  6  7  8  9  10  11 */



if(y%4==0)   //闰年判断 是闰年 天数+1

{

if((y%100!=0)&&(y%400==0))

{

if(m>2)

{

t=t+1;

}

}

}

printf("%d",t);
return 0;

}
楼主看下这个可以不?还有判断闰年的时候应该是这个if((y%100!=0)&&(y%400==0));
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2012-3-28 09:40:59 | 显示全部楼层
819551413 发表于 2012-3-27 19:02
/**************************

题目:输入某年某月某日,判断这一天是这一年的第几天?

不行如果输入是字母就刷屏的提示  感谢你
还有闰年的那个地方 如果能被4整除 而且被100整除还要能被400整除的才是闰年应该是( y%100==0&&y%400==0)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2012-3-29 12:28:14 | 显示全部楼层
看看!!!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2012-3-29 19:17:57 | 显示全部楼层
本帖最后由 莫名其妙 于 2012-3-29 19:28 编辑
仰望天上的光 发表于 2012-3-29 14:50
示意代码


感谢抛砖

我自己来引玉一下 下面是网上找的!~好长好长的内容辩论 留给我自己看了!~
/***********
关于fflush(stdin);
************/

首先请看以下程序:
#include <stdio.h>
int main( void )
{
    int i=1;
    while(i)
    {
        printf("Please input an integer: ");
        scanf("%d", &i);
        printf("%d\n", i);
    }
    return 0;
}

这个程序首先会提示用户输入一个整数,然后等待用户输入,如果用户输入的是整数,
程序会输出刚才输入的整数,并且再次提示用户输入一个整数,然后等待用户输入。
但是一旦用户输入的不是整数(如小数或者字母),假设 scanf 函数最后一次得到的整数
是 2 ,那么程序会不停地输出“Please input an integer: 2”。这是因为scanf("%d", &i);
只能接受整数,如果用户输入了字母,则这个字母会遗留在“输入缓冲区”中。
因为缓冲中有数据,故而 scanf 函数不会等待用户输入,直接就去缓冲中读取,可是缓冲中的却是字母,
这个字母再次被遗留在缓冲中,如此反复,从而导致不停地输出“Please input an integer: 2”。
也许有人会说:“居然这样,那么在 scanf 函数后面加上‘fflush(stdin);’,
把输入缓冲清空掉不就行了?”然而这是错的!C和C++的标准里从来没有定义过 fflush(stdin)。
也许有人会说:“可是我用 fflush(stdin) 解决了这个问题,你怎么能说是错的呢?”的确,
某些编译器(如VC6)支持用 fflush(stdin) 来清空输入缓冲,但是并非所有编译器都要支持
这个功能(linux 下的 gcc 就不支持),因为标准中根本没有定义 fflush(stdin)。MSDN 文档
里也清楚地写着fflush on input stream is an extension to the C standard(fflush 操作
输入流是对 C 标准的扩充)。当然,如果你毫不在乎程序的移植性,用 fflush(stdin) 也没什么
大问题。
/*************************************
以下是 C99 对 fflush 函数的定义:

int fflush(FILE *stream);

如果 stream 指向输出流或者更新流(update stream),并且这个更新流
最近执行的操作不是输入,那么 fflush 函数将把这个流中任何待写数据传送至
宿主环境(host environment)写入文件。否则,它的行为是未定义的。

原文如下:

int fflush(FILE *stream);

If stream points to an output stream or an update stream in which
the most recent operation was not input, the fflush function causes
any unwritten data for that stream to be delivered to the host environment
to be written to the file; otherwise, the behavior is undefined.

其中,宿主环境可以理解为操作系统或内核等。

由此可知,如果 stream 指向输入流(如 stdin),那么 fflush 函数的行为是不确定的。
故而使用 fflush(stdin) 是不正确的,至少是移植性不好的。  
***********************************/


2.清空输入缓冲区的方法

虽然不可以用 fflush(stdin),但是我们可以自己写代码来清空输入缓冲区。只需要在 scanf 函数
后面加上几句简单的代码就可以了。

/* C 版本 */
#include <stdio.h>

int main( void )
{
    int i, c;
    for ( ; ; )
    {
        printf("Please input an integer: ");
        scanf("%d", &i);
        
        if ( feof(stdin) || ferror(stdin) )
        {
            /* 如果用户输入文件结束标志(或文件已被读完), */
            /* 或者发生读写错误,则退出循环               */
            /* do something */
            break;
        }
        /* 没有发生错误,清空输入流。                 */
        /* 通过 while 循环把输入流中的余留数据“吃”掉 */
        while ( (c = getchar()) != '\n' && c != EOF ) ;
        /* 使用 scanf("%*[^\n]"); 也可以清空输入流, */
        /* 不过会残留 \n 字符。                          */
        printf("%d\n", i);
    }
    return 0;
}

/* C++ 版本 */
#include <iostream>
#include <limits> // 为了使用numeric_limits

using std::cout;
using std::endl;
using std::cin;
using std::numeric_limits;
using std::streamsize;

int main()
{
    int value;
    for ( ; ; )
    {
        cout << "Enter an integer: ";
        cin >> value;
        if ( cin.eof() || cin.bad() )
        {
            // 如果用户输入文件结束标志(或文件已被读完),
            // 或者发生读写错误,则退出循环
            // do something
            break;
        }
        // 读到非法字符后,输入流将处于出错状态,
        // 为了继续获取输入,首先要调用 clear 函数
        // 来清除输入流的错误标记,然后才能调用
        // ignore 函数来清除输入流中的数据。
        cin.clear();
        // numeric_limits<streamsize>::max() 返回输入缓冲的大小。
        // ignore 函数在此将把输入流中的数据清空。
        // 这两个函数的具体用法请读者自行查询。
        cin.ignore( numeric_limits<streamsize>::max(), '\n' );
        cout << value << '\n';
    }
    return 0;
}

这是我在别的论坛看到的!楼主文章的观点不对!误导人!!

1.       为什么 fflush(stdin) 是错的
-----------------------------------------
C和C++的标准里从来没有定义过 fflush(stdin)。
---------------------------------------------
错误,不能说fflush(stdin)是错的。作者列出了标准的内容,这显示作者的确有看过标准,但对标准的内容理解错误。标准指出fflush用于输入流的结果是未定义的,但是未定义并不等于是错误!同时c和c++的标准也并非从来没有定义过fflush(stdin),恰恰相反,标准说fflush用于输入流的结果是未定义的本身就是对fflush(stdin)的定义!就是对fflush(stdin)提出的规定!只不过,其结果是未定义而已!

结论应该是:使用fflush(stdin)会产生移植性问题,是不良风格代码,但不是错误。

作者所提出的解决方案:

if ( scanf("%d", &i) != EOF ) {
            while ( (c=getchar()) != '\n' && c != EOF ) {
                  ;
            }
}

并没有完全解决了问题,存在重大的漏洞。主要问题在于,使用getchar()这种方法并没有清除EOF标志。如果用tc2.0、tc2.01、tc3.0、tc3.1等等编译器运行上述代码,输入时用ctrl+z结尾或者直接输入ctrl+z,程序肯定会进入一个死循环!

原因就是getchar()方式并没有清除EOF标志,我在这里所说的EOF标志并非指函数返回的EOF,而是指当I/O函数遇到EOF时在其内部产生的EOF标志。

偶推荐用rewind(stdin)这个方法,rewind不仅清除了stdin中的内容,还清除EOF标志,用下列语句:

scanf("%d", &i);
rewind(stdin);

代替上述if语句,无论你如何输入ctrl+z,都不会进入死循环,同时也简单得多,是比较完美的解决方法。




首先感谢您的评论,它促使我重新审视了我这篇文章,并且修正了文中的一些错漏。特别是文中的两个程序,如果 stdin 被重定向到文件时,会出现死循环。现在我已经把这个问题修正了,就算 stdin 被重定向到文件,也不会出现死循环。如果本文还有其它不足之处,敬请指出,我将不吝感激!

然后,对楼上的一些观点不敢苟同,在此发表一些浅见。

1. 按照楼上对错误的定义,我说 fflush(stdin) 是错的的确是错了。不过,每个人对错误的理解都不一样。我认为,如果某种功能明明可以用标准代码实现,而放着不用,或者不会用,却依赖编译器/系统特定的功能实现,这就是错误。当然,这只是我的看法。还有,我觉得使用编译器/系统特定的功能(如 fflush(stdin);)不算不良风格代码。我认为不良风格是指代码一大堆一大堆地堆放在一起,没有认真地缩进,也缺乏注释,代码层次不清晰明了,功能模块分工不细,等等。另外,对楼上“标准说fflush用于输入流的结果是未定义的本身就是对fflush(stdin)的定义”这个见解非常钦佩。我觉得这个见解别树一格,非常独到,新颖。楼上的脑筋真灵!我就从来没想过这点,惭愧!

2. 我的方案的确存在问题,谢谢你的指出。但问题并不是你所说的那样,而是出在重定向上。如果 stdin 被重定向到文件,我原来的程序的确会导致死循环。

    楼上说“输入时用ctrl+z结尾或者直接输入ctrl+z,程序肯定会进入一个死循环!”,我用 TC 测试过了,直接输入 ctrl+z 不会死循环,但是输入一些数据后用 ctrl+z 结尾的确会出现死循环。不过这个却是 TC 的问题!请看以下代码:

        #include <stdio.h>

        int main( void )
        {
            int ch;

            while ( getchar() != EOF ) ;
            if ( feof(stdin) )
            {   
                printf("Oh, No! EOF indicator is set now!\n");
            }
            clearerr(stdin);
            if ( !feof(stdin) )
            {   
                printf("Ok! EOF indicator is unset now!\n");
            }
            if ( getchar() == EOF )
            {   
                printf("But why still cannot read from stdin?\n");
            }        

            return 0;
        }

用 TC 编译运行时输入 21312313^Z,得到结果如下:

        21312313^Z
        Oh, No! EOF indicator is set now!
        Ok! EOF indicator is unset now!
        But why still cannot read from stdin?

由此可见,就算没有标注 EOF 标记,如果输入时以 ^Z 结尾,也会导致不能从 stdin 中读取数据!这是 TC 的问题!我原来的程序之所以会在输入以 ^Z 结尾时会出现死循环,就是因为不能从 stdin 中读取数据!至于楼上用了 rewind(stdin); 之后就能从 stdin 中读取数据,看来是 TC 特定的功能!

    不过也要感谢楼上,我因此才发现如果 stdin 被重定向到文件,我的程序会出现死循环。不过当初我写那两个程序也仅仅是为了演示一下如何清空 stdin,并没有考虑太多其它因素。

3. 对于楼上提出的方案表示强烈反对!楼上提出的方案比使用 fflush(stdin); 高明不到哪里去,都是使用了编译器特定的功能。

    首先我们看一下标准对 rewind 函数的定义:

        void rewind(FILE *stream);

            rewind 函数把 stream 指向的流的文件位置标记设置为文件
        开始。如果不考虑它还会清除流的错误标记,则 rewind 函数
        等同于

                (void)fseek(stream, 0L, SEEK_SET);
        
        原文如下:

            The rewind function sets the file position indicator for
        the stream pointed to by stream to the beginning of the
        file. It is equivalent to

                (void)fseek(stream, 0L, SEEK_SET)

        except that the error indicator for the stream is also
        cleared.

    K&R 的 The C Programming Language, Second Edition 干脆就说

        rewind(fp); 等同于 fseek(fp, 0L, SEEK_SET); clearerr(fp);

    由此可见,标准只是说 rewind 可以把流的文件位置标记设置为文件开始,并且清除流的错误标记,却没有定义 rewind(stdin) 可以清空 stdin 的内容,所以使用 rewind(stdin) 不一定能清空 stdin。而且,如果 stdin 被重定向到文件的话,使用 rewind 更是会产生非常“有趣”的结果。有兴趣的朋友可以试一下。


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2012-3-29 19:34:15 | 显示全部楼层
嗯,确实不具有移植性,但是写惯了,改不了了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 21:49

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表