终端密码输入问题

【问题】

分别在dos和linux终端下实现一个密码输入程序,要求:

1.      输入字符用“*”替代;

2.      输错了可以用退格键删除;

3.      输入字符不包括方向键、制表符、ESC、翻页键、Home、End等极特殊字符。



【基础知识】

输入密码时,输入一个字符立即回显为“*”,这就要求输入是无缓冲的。

终端本身也是个输入和输出的设备,它也是带有缓冲的,Terminal默认是行缓冲的。所以需要同时将stdin和终端的缓冲都设置为无缓冲才可以。C标准库是做不到的。

而且,取消缓冲机制,但同时也要提供错误修改功能,也么只能手工实现缓冲机制,比如将输入的密码暂存在一个数组里。

Windows下由conio.h提供的getch和getche函数来实现,Linux下可由termios.h库来实现。


【实现代码】

  • Windows

#include <stdio.h>
#include <conio.h>
#include <string.h>

#define BACKSPACE 0x08
#define BELL      0x07

char *get_password(const char *prompt)
{
    static char buffer[128];
    //i指向buffer数组未赋值的下一个字符
    int i = 0;
    char letter = 0;

    memset(buffer, 0, sizeof(buffer));
    printf(prompt);
    while((i<127)&&(letter!='\r'))
    {
        letter = getch();
        if(letter==BACKSPACE)//退格
        {
            if(i>0)
            {
                buffer[--i] = 0;
                putchar(BACKSPACE);
                putchar(' ');//覆盖掉*
                putchar(BACKSPACE);
            }
            else putchar(BELL);//警告已无法后退
        }
        else if(letter!='\r')
        {
            buffer[i++]=letter;
            putchar('*');
        }
    }
    return buffer;
}

int main()
{
    char *password;
    password = get_password("Enter password:");
    printf("\n%s\n",password);
    printf("press any key to continue.");
    getch();
    return 0;
}

  • Linux

#include <stdio.h>
#include <termios.h>
#include <string.h> //提供memset的声明
#include <unistd.h> //提供STDIN_FILENO的定义
 
#define BACKSPACE 0x08
#define BELL      0x07
 
/**
获取密码
prompt是提示字符串
*/
char *get_password(constchar *prompt)
{
    static char buffer[128];//存储密码字符串
    int i = 0;//i指向buffer数组未赋值的下一个字符
    char letter = 0;
 
    memset(buffer, 0, sizeof(buffer));
    printf(prompt);
   
    while(i<127)
    {
        letter = getc(stdin);
        if(letter==0x7f)//退格键,回显为^?,所以需要退格三次
        {
            if(i>0)
            {
                buffer[--i] = 0;
                putchar(BACKSPACE);putchar(BACKSPACE);putchar(BACKSPACE);
                putchar(' ');putchar('');putchar(' ');
                putchar(BACKSPACE);putchar(BACKSPACE);putchar(BACKSPACE);
            }
            else//退无可退
            {
                putchar(BACKSPACE);putchar(BACKSPACE);
                putchar(' ');putchar(' ');
                putchar(BACKSPACE);putchar(BACKSPACE);
                putchar(BELL);
            }
            continue;
        }
        else if(letter == '\n')//输入结束
        {
            break;
        }
        else//有效字符
        {
            buffer[i++] = letter;
            putchar(BACKSPACE);
            putchar('*');
        }
    }
    return buffer;
}
 
/**
用来设置终端无缓冲和恢复终端设置
setnonebuf为非0,则设置终端无缓冲;为0,恢复终端原设置
*/
void set_terminal_buf(int setnonebuf)
{
    static struct termios old_tio, new_tio;
    static int ismodified = 0; //用来防止错误的恢复原来的设置
 
    if(setnonebuf == 0)
    {
        if(ismodified == 0) return; //还未修改过,所以不能恢复
        tcsetattr(STDIN_FILENO, TCSANOW,&old_tio);
        ismodified = 0;
        return;
    }
 
    tcgetattr(STDIN_FILENO, &old_tio);
    new_tio = old_tio;
    new_tio.c_lflag &= ~ICANON;
    tcsetattr(STDIN_FILENO, TCSANOW,&new_tio);
    ismodified = 1;
}
 
int main()
{
    set_terminal_buf(1);
    setbuf(stdin, NULL);
 
    char *password;
    password = get_password("Enterpassword:");
    printf("%s\n",password);
 
    set_terminal_buf(0);
 
    return 0;
}


输入退格键,dos下的字符是0x08,linux下的字符是0x7F。


【参考文献】

http://www.dewen.io/q/7366

你可能感兴趣的:(Terminal,密码输入)