`
boisterous
  • 浏览: 62653 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类

获取用户输入的问题(清除stdin)

阅读更多
需求:
        从终端获取用户输入的用户名和密码(有效长度为n),我把 buffer 设为 n+10,这样当用
        户输入过长时可以接收到多余的字符,从而判断出超长了;如果用户直接按回车键,那么应该
        接收到0个字符。

函数:
        fgets( buf, buf_size, stdin)

问题:
        当用户输入超过 buf_size 时,下次再调用 fgets(),它会直接返回而不是等待用户输入,
        因为 fgets() 是直接从 stdin 拿字符的,上次没有取完的字符,直接被 fgets() 拿走然
        后返回;

网上的解决方法:
        1. 使用fflush()。可惜没有任何作用,虽然在Windows下可以,但Linux下没有这个效果。

        2. 调用完 fgets() 后用 while( (ch = getchar() ) != '\n' && ch != EOF );
        清除 stdin 中剩余的字符。问题是如果用户输入小于buf_size, fgets() 成功返回后,
        又进入 getchar() 等待用户输入,用户必须至少多按一次回车键才能得到他想要的结果。

        3. 使用 C++ 的 cin>>buf, 然后再调用 cin.clear() 清除stdin。 这次 stdin 是真的
        可以成功清除了,解决了多余字符问题,但是如果你直接回车,cin>>buf 是不会返回的,
        必须至少输入一个字符后再回车才行,与需求不符。

        4. ......

我的方法:
        1. fgets() 后,直接 stdin->_IO_read_ptr = stdin->_IO_read_end; 即可解决所有
        问题。stdin 实际上是一个结构体指针,如下:
        头文件: /usr/include/stdio.h

view plaincopy to clipboardprint?
/* Standard streams.  */ 
extern struct _IO_FILE *stdin;        /* Standard input stream.  */ 
extern struct _IO_FILE *stdout;        /* Standard output stream.  */ 
extern struct _IO_FILE *stderr;        /* Standard error output stream.  */  


        看了glibc的源码后发现 fgets() 只使用 _IO_FILE 中的 _IO_read_end 和 _IO_read_ptr 两个指针而已。
        当然,还需要 #include <libbio.h> 因为 _IO_FILE 在这个头文件中定义的。具体如下:
        头文件: /usr/include/libio.h


view plaincopy to clipboardprint?
struct _IO_FILE { 
//...... 
/* The following pointers correspond to the C++ streambuf protocol. */ 
/* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ 
char* _IO_read_ptr;    /* Current read pointer */ 
char* _IO_read_end;    /* End of get area. */ 
char* _IO_read_base;    /* Start of putback+get area. */ 
char* _IO_write_base;    /* Start of put area. */ 
char* _IO_write_ptr;    /* Current put pointer. */ 
char* _IO_write_end;    /* End of put area. */ 
char* _IO_buf_base;    /* Start of reserve area. */ 
char* _IO_buf_end;    /* End of reserve area. */ 
/* The following fields are used to support backing up and undo. */ 
char *_IO_save_base; /* Pointer to start of non-current get area. */ 
char *_IO_backup_base;  /* Pointer to first valid character of backup area */ 
char *_IO_save_end; /* Pointer to end of non-current get area. */ 
//...... 
}  



        2. 也许还有其它方法......

注意:

        使用fgets(), 如果用户输入小于 buf_size-1(最后一个字符空间留给'\0'),则获取的字符串以'\n'结尾,

        也就是说,fgets()自动把用户输入的回车字符放进去了。

===============================================================================

view plaincopy to clipboardprint?
// 
// 等待用户输入字符串 
// echo - 是否回显用户输入? 
// 
void wait_input( char *buf, int buf_size, int echo ) 

        struct termios old_val, new_val; 
        tcgetattr( fileno( stdin ), &old_val ); 
        new_val = old_val; 
        if( echo ) 
                new_val.c_lflag |= ( ECHO ); 
        else 
                new_val.c_lflag &= ( ~ECHO ); 
        tcsetattr( fileno( stdin ), TCSANOW, &new_val ); 
        fgets( buf, buf_size, stdin); 
        // 起到冲刷stdin的效果(glibc) 
        stdin->_IO_read_ptr = stdin->_IO_read_end; 
        tcsetattr( fileno( stdin ), TCSANOW, &old_val ); 
        // 如果输入不够长,字符串最后有一个'\n' 
        if( '\n' == buf[ strlen(buf) - 1 ] ) 
                buf[ strlen(buf) - 1 ] = '\0'; 
        // 禁止回显后,输入结束时不会自动换行 
        if( !echo ) 
                printf( "\n" ); 
        return; 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics