获取用户输入的问题(清除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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值