眾所周知,一個良好的密碼輸入程序是在用戶輸入密碼時不顯示密碼本身,只回顯星號。或者,在安全性要求更高的某些程序中,什麼都不顯示。那麼,在C語言中如何實現它呢?
getc()和getchar()函數想必大家都經常用到,但它們都在輸入的同時顯示輸入內容,並由回車終止輸入。為了不顯示輸入內容,我們調用另外一個函數getch(),它包含在<conio.h>頭文件中。該函數可以在輸入的同時不顯示輸入內容,並在輸入完成後不需回車而自動終止輸入。與此同時,該頭文件中還包含另外一個函數getche(),它和getch()功能相同,唯一的區別是輸入的同時顯示輸入的內容。
//先來看一個網上出現最多的版本,這個函數有哪些不合理的地方?怎麼改進?
#include <stdio.h>
#include <conio.h>
char passwd[64]="";
void getPasswd(const char *prompt)
{
char c;
int i = 0;
puts(prompt);
while ((c=getch()) != '\r')
{
passwd[i++] = c;
putchar('*');
}
passwd[i] = '\0';
printf("\n");
}
//下面是我改進的版本,還有哪些不足的地方,請大家指出,謝謝了!
//原型:char *getPasswd(const char *prompt);
//功能:提示用戶輸入密碼,並將密碼保存在static char[]裡
//參數:const char* prompt 提示字符串
//返回:返回以null結尾的密碼字符串指針(函數內的static char [])
//注意:如果密碼要留著用的話,要用strcpy保存起來,否則再次調用getPasswd()時,static char[]會被新的密碼覆蓋
//該函數調用了getch(),而UNIX下似乎不支持此函數,文章後面給出了替代方案。。。
#include<stdio.h>
#include<string.h>
#include<conio.h>
#define MAX_LEN 8
#define BACKSPACE 8
#define ENTER 13
#define ALARM 7
char *getPasswd(const char *prompt)
{
int i=0, ch;
static char p[MAX_LEN+1]="";
printf("%s", prompt);
while((ch = getch())!= -1 && ch != ENTER)
{
if(i == MAX_LEN && ch != BACKSPACE)
{
putchar(ALARM);
continue;
}
if(ch == BACKSPACE)
{
if(i==0)
{
putchar(ALARM);
continue;
}
i--;
putchar(BACKSPACE);
putchar(' ');
putchar(BACKSPACE);
}
else
{
p[i] = ch;
putchar('*');
i++;
}
}
if(ch == -1)
{
while(i != -1)
{
p[i--] = '\0';
}
return NULL;
}
p[i]='\0';
printf("\n");
return p;
}
//可惜上面的版本在我的Linux虛擬機上跑不起來,說不認識conio.h,沒招,繼續探索...
//於是找到了下面的版本...
getch()是conio.h中提供的,標准C裡面沒有。
如果需要在Linux和VC裡面同時編譯,用到getch()的地方就這麼處理:
#ifndef _WIN32 //Linux platform
#include <termio.h>
#define getch() get_char(0) //without stdout
#define getche() get_char(1) //with stdout
#else //WIN32 platform
#include <conio.h>
#endif
#ifndef _WIN32 //Linux platform
/************************************************************************
* author:hzcpig
* method name: get_char
* input: 0--getch() 1--getche()
* return: char
* remark: Linux simulative gech(),getche()
Instead of "conio.h" using in VC6.0 environment
************************************************************************/
char get_char(int flag)
{
char ch;
struct termios new_settings;
struct termios stored_settings;
tcgetattr(0, &stored_settings);
new_settings = stored_settings;
new_settings.c_lflag &= (~ICANON);
new_settings.c_cc[VTIME] = 0;
tcgetattr(0, &stored_settings);
new_settings.c_cc[VMIN] = 1;
tcsetattr(0, TCSANOW, &new_settings);
if (flag == 1) //getche()
{
ch = getchar();
}
else //getch()
{
system("stty -echo ");
ch = getchar();
system("stty echo ");
tcsetattr (0, TCSANOW, &stored_settings);
}
return ch;
}
#endif
//可惜我編排了半天的格式,最後一運行,發現上面的版本在我的機子上跑起來停不下來,沒招,繼續探索
//下面這個版本跑不起來,不過似乎是高手寫的,所以留著,以後看得懂了再看看為什麼跑不起來,繼續。。。
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
int getch()
{
char ch;
struct termios save, ne;
ioctl(0, TCGETS, &save);
ioctl(0, TCGETS, &ne);
ne.c_lflag &= ~(ECHO | ICANON);
ioctl(0, TCSETS, &ne);
read(0, &ch, 1);
ioctl(0, TCSETS, &save);
return ch;
}
//終於讓我找到一個可以替代的,不過還是看不懂,而且只是經過簡單的測試。。。
#include <stdio.h>
#include <termio.h>
int getch(void)
{
struct termios tm, tm_old;
int fd = STDIN_FILENO, c;
if(tcgetattr(fd, &tm) < 0)
return -1;
tm_old = tm;
cfmakeraw(&tm);
if(tcsetattr(fd, TCSANOW, &tm) < 0)
return -1;
c = fgetc(stdin);
if(tcsetattr(fd, TCSANOW, &tm_old) < 0)
return -1;
return c;
}
//於是完整的在linux和windows下通用的版本出來了......
#include <stdio.h>
#ifndef _WIN32 //Linux platform
#include <termio.h>
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
int getch(void)
{
struct termios tm, tm_old;
int fd = STDIN_FILENO, c;
if(tcgetattr(fd, &tm) < 0)
return -1;
tm_old = tm;
cfmakeraw(&tm);
if(tcsetattr(fd, TCSANOW, &tm) < 0)
return -1;
c = fgetc(stdin);
if(tcsetattr(fd, TCSANOW, &tm_old) < 0)
return -1;
return c;
}
#else //WIN32 platform
#include <conio.h>
#endif
#define MAX_LEN 8
#define BACKSPACE 8
#define ENTER 13
#define ALARM 7
char *getPasswd(const char *prompt)
{
int i=0, ch;
static char p[MAX_LEN+1]="";
printf("%s", prompt);
while((ch = getch())!= -1 && ch != ENTER)
{
if(i == MAX_LEN && ch != BACKSPACE)
{
putchar(ALARM);
continue;
}
if(ch == BACKSPACE)
{
if(i==0)
{
putchar(ALARM);
continue;
}
i--;
putchar(BACKSPACE);
putchar(' ');
putchar(BACKSPACE);
}
else
{
p[i] = ch;
putchar('*');
i++;
}
}
if(ch == -1)
{
while(i != -1)
{
p[i--] = '\0';
}
return NULL;
}
p[i]='\0';
printf("\n");
return p;
}
int main()
{
char *pw = getPasswd("passwd:");
puts(pw);
puts("clearing the static buffer with 0 ...");
while(*pw)
{
*pw++=0;
}
pw=NULL;
return 0;
}
//其實在UNIX下有一個函數,直接就是用來輸入密碼用的,我們每次登陸的時候都在調用。。。
//但手冊上說該函數已經被遺棄,不過UNIX環境高級編程給出了他的一個實現,未完待續。。。
//如果需要對密碼加密,可以查一下crypt函數的用法