尝试将用户输入实时转换为星号

发布于 2025-01-12 03:21:52 字数 944 浏览 2 评论 0原文

我正在编写一个小型 C 程序,要求用户输入密码,当用户通过键盘输入字符时,这些字符将显示为星号,当他们按下 Enter 键时,将显示实际密码。我尝试摆弄 getchar() 并分配变量,但没有得到所需的解决方案。任何指导表示赞赏,谢谢。

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>

char *get_password();

int main() {
    struct termios info;

    tcgetattr(0, &info);
    // info.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &info);

    printf("Create a password: ");
    int c = getchar();
    c = '*';
    char *password = get_password();
    printf("You entered: %s\n", password);

    tcgetattr(0, &info);
    info.c_lflag |= ECHO;
    tcsetattr(0, TCSANOW, &info);
}

#define BUFSIZE 100
char buf[BUFSIZE];

char *get_password() {
    int c, len = 0;
    while ((c = getchar()) != EOF && c != '\n') {
        buf[len++] = c;
        if (len == BUFSIZE - 1)
            break;
    }
    buf[len] = 0;
    putchar('\n');
    return buf;
}

I am writing a small C program that asks a user for a password, as the user enters characters via keyboard, these characters will be displayed as asterisks and when they hit enter the actual password is displayed. I have tried fiddling with getchar() and assigning variables but I am not getting the desired solution. Any guidance is appreciated, thanks.

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>

char *get_password();

int main() {
    struct termios info;

    tcgetattr(0, &info);
    // info.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &info);

    printf("Create a password: ");
    int c = getchar();
    c = '*';
    char *password = get_password();
    printf("You entered: %s\n", password);

    tcgetattr(0, &info);
    info.c_lflag |= ECHO;
    tcsetattr(0, TCSANOW, &info);
}

#define BUFSIZE 100
char buf[BUFSIZE];

char *get_password() {
    int c, len = 0;
    while ((c = getchar()) != EOF && c != '\n') {
        buf[len++] = c;
        if (len == BUFSIZE - 1)
            break;
    }
    buf[len] = 0;
    putchar('\n');
    return buf;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼眸印温柔 2025-01-19 03:21:52

正如 @chqrlie 指出的,正确的密码处理需要禁用回显和缓冲。以下是执行此操作的快速步骤:

#include <termios.h>
#include <sys/ioctl.h>

struct termios termstat;
tcgetattr(STDIN_FILENO,&termstat);
termstat.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO,TCSANOW,&termstat);

完成密码处理后,使用以下命令恢复正常行为:

termstat.c_lflag |= (ICANON | ECHO);
tcsetattr(STDIN_FILENO,TCSANOW,&termstat);

As @chqrlie points out, proper password processing requires that echoing be disabled and buffering as well. Here are the quick steps to do that:

#include <termios.h>
#include <sys/ioctl.h>

struct termios termstat;
tcgetattr(STDIN_FILENO,&termstat);
termstat.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO,TCSANOW,&termstat);

then after you're done with the password handling, restore the normal behavior with:

termstat.c_lflag |= (ICANON | ECHO);
tcsetattr(STDIN_FILENO,TCSANOW,&termstat);
八巷 2025-01-19 03:21:52

读取没有 echo 的密码比使用 getchar()摆弄要复杂一些:

  • 您必须禁用 stdin 中的缓冲或直接读取字节来自低层系统句柄 0*
  • 您必须使用系统相关的 API 调用暂时禁用终端回显和缓冲功能;
  • 为了避免使终端处于不良状态,您还应该禁用信号处理并显式处理信号字符;
  • 从句柄 0 读取字节后,必须输出 star 并将输出刷新到终端;
  • 您应该处理退格键,因为用户希望能够纠正打字错误;
  • 输入完成后必须恢复终端状态。

*) 从系统句柄 2 (stderr) 读取是从用户获取密码的有用替代方法,即使 stdin 是从文件重定向的。

这是一个带注释的实现:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

char *get_password(void) {
    struct termios info, save;
    static char buf[100];
    size_t len = 0;
    char c;
    int res;

    tcgetattr(0, &info);
    save = info;

    /* input modes: no break, no CR to NL, no parity check, no strip char,
     * no start/stop output control. */
    info.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
                      INLCR | IGNCR | ICRNL | IXON);
    /* output modes - disable post processing */
    info.c_oflag &= ~(OPOST);
    /* local modes - echoing off, canonical off, no extended functions,
     * no signal chars (^Z,^C) */
    info.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
    /* control modes - set 8 bit chars, disable parity handling */
    info.c_cflag &= ~(CSIZE | PARENB);
    info.c_cflag |= CS8;
    /* control chars - set return condition: min number of bytes and timer.
     * We want read to return every single byte, without timeout. */
    info.c_cc[VMIN] = 1;   /* 1 byte */
    info.c_cc[VTIME] = 0;  /* no timer */
    tcsetattr(0, TCSANOW, &info);

    for (;;) {
        fflush(stdout);
        /* read a single byte from stdin, bypassing stream handling */
        res = read(0, &c, 1);
        if (res != 1) {
            /* special case EINTR and restart */
            if (res == -1 && errno == EINTR)
                continue;
            /* other cases: read failure or end of file */
            break;
        }
        if (c == '\n' || c == '\r') {
            /* user hit enter */
            break;
        }
        if (c == '\b' || c == 127) {
            /* user hit the backspace or delete key */
            if (len > 0) {
                printf("\b \b");
                len--;
            }
            continue;
        }
        if (c == 3) {
            /* user hit ^C: should abort the program */
            tcsetattr(0, TCSANOW, &save);
            printf("^C\n");
            fflush(stdout);
            return NULL;
        }
        if (c < ' ') {
            /* ignore other control characters */
            continue;
        }
        if (len >= sizeof(buf) - 1) {
            putchar(7);     /* beep */
        } else {
            putchar('*');
            buf[len++] = c;
        }
    }
    tcsetattr(0, TCSANOW, &save);
    putchar('\n');
    fflush(stdout);

    buf[len] = '\0';
    return buf;
}

int main() {
    char *password;
    printf("Create a password: ");
    password = get_password();
    if (password) {
        printf("You entered: %s\n", password);
    } else {
        printf("You hit ^C, program aborted\n");
        return 1;
    }
    return 0;
}

Reading a password without echo is a bit more tricky than just fiddling with getchar():

  • you must disable the buffering in stdin or read bytes directly from the low level system handle 0*;
  • you must temporarily disable the terminal echo and buffering features, using system dependent API calls;
  • to avoid leaving the terminal in a bad state, you should also disable signal processing and handle the signal characters explicitly;
  • upon reading the bytes from handle 0, you must output stars and flush the output to the terminal;
  • you should handle the backspace key as users will expect to be able to correct typing errors;
  • you must restore the terminal state after input completes.

*) Reading from system handle 2 (stderr) is a useful alternative to get the password from the user even if stdin is redirected from a file.

Here is a commented implementation:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>

char *get_password(void) {
    struct termios info, save;
    static char buf[100];
    size_t len = 0;
    char c;
    int res;

    tcgetattr(0, &info);
    save = info;

    /* input modes: no break, no CR to NL, no parity check, no strip char,
     * no start/stop output control. */
    info.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
                      INLCR | IGNCR | ICRNL | IXON);
    /* output modes - disable post processing */
    info.c_oflag &= ~(OPOST);
    /* local modes - echoing off, canonical off, no extended functions,
     * no signal chars (^Z,^C) */
    info.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
    /* control modes - set 8 bit chars, disable parity handling */
    info.c_cflag &= ~(CSIZE | PARENB);
    info.c_cflag |= CS8;
    /* control chars - set return condition: min number of bytes and timer.
     * We want read to return every single byte, without timeout. */
    info.c_cc[VMIN] = 1;   /* 1 byte */
    info.c_cc[VTIME] = 0;  /* no timer */
    tcsetattr(0, TCSANOW, &info);

    for (;;) {
        fflush(stdout);
        /* read a single byte from stdin, bypassing stream handling */
        res = read(0, &c, 1);
        if (res != 1) {
            /* special case EINTR and restart */
            if (res == -1 && errno == EINTR)
                continue;
            /* other cases: read failure or end of file */
            break;
        }
        if (c == '\n' || c == '\r') {
            /* user hit enter */
            break;
        }
        if (c == '\b' || c == 127) {
            /* user hit the backspace or delete key */
            if (len > 0) {
                printf("\b \b");
                len--;
            }
            continue;
        }
        if (c == 3) {
            /* user hit ^C: should abort the program */
            tcsetattr(0, TCSANOW, &save);
            printf("^C\n");
            fflush(stdout);
            return NULL;
        }
        if (c < ' ') {
            /* ignore other control characters */
            continue;
        }
        if (len >= sizeof(buf) - 1) {
            putchar(7);     /* beep */
        } else {
            putchar('*');
            buf[len++] = c;
        }
    }
    tcsetattr(0, TCSANOW, &save);
    putchar('\n');
    fflush(stdout);

    buf[len] = '\0';
    return buf;
}

int main() {
    char *password;
    printf("Create a password: ");
    password = get_password();
    if (password) {
        printf("You entered: %s\n", password);
    } else {
        printf("You hit ^C, program aborted\n");
        return 1;
    }
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文