【问题标题】:(C language) How can i use backspace while using getchar(); in this function?(C 语言)如何在使用 getchar() 时使用退格键;在这个函数中?
【发布时间】:2018-11-11 12:01:43
【问题描述】:

如果我无法理解,您好抱歉,我是 c 编程新手,我不是最好的英语作家。

我的问题:我不明白如何在使用代码时使用退格键,如果有人能解释一下它是如何工作的,我很高兴。

    #include <stdio.h>
int main()
{
    char name[30], ch;
    int i = 0;
    printf("Enter name: ");
    while(ch != '\n')    // terminates if user hit enter
    {
        ch = getchar();
        name[i] = ch;
        i++;
    }
    name[i] = '\0';       // inserting null character at end
    printf("Name: %s", name);
    return 0;
}

当我运行这个程序时,我实际上可以写下我的名字,而在我写的时候,我可以使用退格键删除字符然后继续写,这怎么可能?因为据我了解,此代码在我点击后立即输入任何字符来命名数组。 谢谢你,乔纳坦。

【问题讨论】:

  • 有允许用户输入字符串的标准和外部库函数。您为什么不使用其中之一?
  • 如果你想要一个简单的backspace,那么ASCII字符8是退格字符。它被使用了两次。调用putchar (8) 退回字符,然后调用putchar (' ') 清除字符,然后再次调用putchar (8) 将光标定位在已删除字符之前。简单、基本、有效。在大多数 x 术语上,要捕获退格字符的“键”是 ASCII 127。 (del)
  • @DavidC.Rankin 你的意思是,当我按下键盘上的退格键时,这就是发生的过程。或者你只是解释我在使用 getchar(); 时如何使用退格?
  • @usr2564301 它们是哪些函数?我讨厌 scanf() 因为它有很多限制。
  • 当你输入'ab'时,你的程序什么都看不到。然后当你点击 时,你的程序将得到 2 个字符 'a' 和一个换行符。终端正在为你做很多缓冲。

标签: c


【解决方案1】:

Yoni,你有两个很好的答案,但为了完整起见,我将在 cmets 中提供你有问题的其余信息。

首先,在接受您想要显示的任何输入并允许最少的用户编辑时,您需要将您的键盘置于非规范模式,以便您的程序可以使用每个按键作为键已键入 -- 无需等待用户按 Enter。您可以使用tcgetattr(终端获取属性)和tcsetattr(终端设置属性)来处理这个问题。 *非规范模式是 windows getch() 提供的。

基本上,您将读取循环设置为使用getcharfgetc 读取(如果您希望能够从stdin 或文件中读取值。)您可以通过以下方式控制读取循环:

#define MAXPW 32    /* constant for max input length */

int main (int argc, char **argv) {

    int c,
        idx = 0;                        /* buf index */
    char pw[MAXPW] = "",                /* buf for passwd */
        mask = argc > 1 ? *argv[1] : 0; /* mask off by default */
    FILE *fp = stdin;
    ...
    /* read chars from fp, mask w/mask char */
    while ((idx + 1 < MAXPW && (c = fgetc (fp)) != '\n' && c != EOF) ||
            (idx == MAXPW - 1 && c == 127))
    {

请注意,当 (1) (space_remains AND c 不是 '\n' @ 987654330@) OR (2) (space_remains AND backspace_key_pressed)

即使您处理这两种情况 (1) 是否是普通字符 - 添加它;或 (2) 是退格字符,然后备份,用space 覆盖字符并再次备份,例如

        if (c != 127) {                 /* not the backspace characters */
            if (' ' - 1 < mask && mask < 127)   /* if mask valid ASCII */
                fputc (mask, stdout);   /* output mask char */
            else
                fputc (c, stdout);      /* output normal char */
            pw[idx++] = c;              /* store char, adv index */
        }
        else if (idx > 0) {             /* handle backspace (del)   */
            fputc (0x8, stdout);        /* backup */
            fputc (' ', stdout);        /* overwrite with space */
            fputc (0x8, stdout);        /* backup again */
            pw[--idx] = 0;              /* nul-termiante after current */
        }

注意:如果您的mask 字符是可打印字符,则输出mask 字符,例如

enter passwd: ********

如果桅杆不可打印(默认为nul),则输出文本。您可以将掩码字符设置为程序的第一个参数(用引号括起来),例如

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

#include <sys/time.h>
#include <termios.h>
#include <errno.h>      /* for errno */
#include <unistd.h>     /* for EINTR */

#define MAXPW 32    /* constant for max input length */

int main (int argc, char **argv) {

    int c,
        idx = 0;                        /* buf index */
    char pw[MAXPW] = "",                /* buf for passwd */
        mask = argc > 1 ? *argv[1] : 0; /* mask off by default */
    FILE *fp = stdin;

    struct termios old_kbd_mode;    /* orig keyboard settings   */
    struct termios new_kbd_mode;

    if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings   */
        fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
        return -1;
    }   /* copy old to new */
    memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));

    /* put keyboard in non-canonical/no echo mode */
    new_kbd_mode.c_lflag &= ~(ICANON | ECHO);  /* new kbd flags */
    new_kbd_mode.c_cc[VTIME] = 0;
    new_kbd_mode.c_cc[VMIN] = 1;
    if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
        fputs ("error: tcsetattr failed.\n", stderr);
        return -1;
    }

    fputs ("enter passwd : ", stdout);  /* set passwd prompt */

    /* read chars from fp, mask w/mask char */
    while ((idx + 1 < MAXPW && (c = fgetc (fp)) != '\n' && c != EOF) ||
            (idx == MAXPW - 1 && c == 127))
    {
        if (c != 127) {                 /* not the backspace characters */
            if (' ' - 1 < mask && mask < 127)   /* if mask valid ASCII */
                fputc (mask, stdout);   /* output mask char */
            else
                fputc (c, stdout);      /* output normal char */
            pw[idx++] = c;              /* store char, adv index */
        }
        else if (idx > 0) {             /* handle backspace (del)   */
            fputc (0x8, stdout);        /* backup */
            fputc (' ', stdout);        /* overwrite with space */
            fputc (0x8, stdout);        /* backup again */
            pw[--idx] = 0;              /* nul-termiante after current */
        }
    }
    pw[idx] = 0; /* null-terminate final string */

    /* restore original keyboard mode */
    if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
        fputs ("error: tcsetattr failed.\n", stderr);
        return -1;
    }

    printf ("\nstored passwd: %s\n", pw);
}

可编辑输入

不带掩码运行程序,假设用户输入:

$ ./bin/backspace
enter passwd : my_password_is_bad

(用户想了想并说“哦,那不好”,现在可以按退格键 3 次让她看着:

$ ./bin/backspace
enter passwd : my_password_is_

现在她完成了她的输入:

$ ./bin/backspace
enter passwd : my_password_is_good
stored passwd: my_password_is_good

操作与显示的掩码字符完全相同。显示所有掩码字符并且用户忘记了她输入的内容,她可以简单地在所有字符显示上退格(如果她愿意,请继续按下退格键,然后再次继续输入正确的密码(姓名,等等)。 mask 字符为 '*' 的示例,例如

$ ./bin/backspace '*'
enter passwd : *******************
stored passwd: my_password_is_good

在某些情况下,这是一种为用户提供最少编辑功能的便捷方式。如果您不需要屏蔽用户输入,那么您可以完全取消更改键盘模式。

检查一下,如果您有任何问题,请告诉我。

【讨论】:

    【解决方案2】:

    用户空间 C 的 stdio 不会直接与硬件对话。它会与操作系统对话。并且操作系统通常会对它收到的击键进行相当多的预处理,然后再将它们发送到应用程序。在类 UNIX 操作系统上,大部分预处理将由您的终端驱动程序完成,可以将其设置为重置为原始模式,在这种情况下,您实际上也会收到退格键。不过,使用终端驱动程序并没有被 C 标准标准化。

    在 Linux 上,我可以做到:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char name[30], ch;
        int i = 0;
        printf("Enter name: ");
        system("stty raw");
        while(ch != '\n' && i < sizeof(name))    // terminates if user hit enter
        {
            ch = getchar();
            name[i] = ch;
            i++;
        }
        name[i] = '\0';       // inserting null character at end
        printf("Name: %s", name);
        system("stty sane"); //set some sane settings to the terminal
        return 0;
    }
    

    然后我得到原始字符(我需要输入shift+Enter 来发送\n)。

    【讨论】:

    • 感谢您的帮助
    【解决方案3】:

    我没有测试代码,但主要思想是这样的:

    while(ch != '\n')    // terminates if user hit enter
    {
        ch = getchar();
        // if this is a backspace character, 
        // lower the index and delete the last char
        if(ch == 0x08){
           name[--i] = 0x00;
        }else{
           // other chars will increment the index and fill the current char buffer
           name[i++] = ch;
        }
    }
    

    后期编辑:

    抱歉,我猜我的问题理解错了。正确答案是这样的:

    假设您在终端中输入:1235[0x08]4

    你的 char 数组是:

    [0x31, 0x02, 0x33, 0x35, 0x08, 0x34] 
    

    当你打印它时,它会像这个顺序一样执行,它会逐个字符地打印。同样,5 会以如此快的速度打印和退格,您不会注意到。

    还有一个问题可能会让您了解退格在某些环境中的工作原理:

    The "backspace" escape character '\b': unexpected behavior?

    【讨论】:

    • 哦,我明白了,所以我实际上在我的数组中输入了退格键?哇,你真的帮了我,在你编辑之前,你实际上给了我一个为什么要这样做的正确原因。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2016-08-19
    • 1970-01-01
    • 1970-01-01
    • 2022-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多