【问题标题】:How can i make getchar() function hold backspace如何让 getchar() 函数保持退格键
【发布时间】:2018-05-10 05:38:57
【问题描述】:
#include<stdio.h>
int main(void)
{
    int c;
    while((c=getchar())!=EOF)
    {
        if(c==8)       // 8 is ASCII value of backspace
          printf("\\b");
    }
}

现在我想输入退格并希望 getchar() 函数将退格的 ASCII 返回给 c(int 变量)

注意-我不是在询问 getch 函数,我知道 getch 命令能够读取退格 我只想知道 getchar 是否能够读取退格键 如果是,如何?

怎么做,请解释一下 我是 C 编程新手

【问题讨论】:

标签: c


【解决方案1】:

如果流 stdin 从文件中读取,getchar() 将像处理任何其他常规字符一样处理退格字符('\b' 或 ASCII 中的 8)并将其返回给调用者。

从控制台(又名 terminaltty)读取时不这样做的原因与控制台本身的配置有关。默认情况下,控制台处于 cooked 模式,以便处理回显、退格和行缓冲,以及为 Ctrl-C 发送的 SIGINT 等信号等等细微设置。

C 标准没有提供任何改变终端模式的方法,但是 POSIX 系统有 stty 命令和 termios 系统调用可供 C 程序执行此操作。

将控制台配置为 raw 模式后,还要将stdin 设置为无缓冲模式,并使用setvbuf(stdin, NULL, _IONBF, 0) 以便getchar() 在输入时从控制台读取每个字节。

配置控制台是一个复杂的问题,您可能需要先阅读以下内容:http://www.linusakesson.net/programming/tty/

【讨论】:

  • 谢谢我得到了合适的答案。
【解决方案2】:

如果您的系统支持 POSIX.1-2001 中标准化的 termios,那么您可以操作标准输入终端以不缓冲您的输入。考虑以下示例:

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* SIGINT handler */

static volatile sig_atomic_t  done = 0;

static void handle_done(int signum)
{
    if (!done)
        done = signum;
}

static int install_done(const int signum)
{
    struct sigaction  act;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_handler = handle_done;
    act.sa_flags = 0;
    if (sigaction(signum, &act, NULL) == -1)
        return errno;
    return 0;
}

/* Reverting terminal back to original settings */

static struct termios  terminal_config;

static void revert_terminal(void)
{
    tcsetattr(STDIN_FILENO, TCSAFLUSH, &terminal_config);
}


int main(void)
{
    int  c;

    /* Set up INT (Ctrl+C), TERM, and HUP signal handlers. */
    if (install_done(SIGINT) ||
        install_done(SIGTERM) ||
        install_done(SIGHUP)) {
        fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    /* Make terminal input noncanonical; not line buffered. Also disable echo. */
    if (isatty(STDIN_FILENO)) {
        struct termios  config;
        if (tcgetattr(STDIN_FILENO, &terminal_config) == 0 &&
            tcgetattr(STDIN_FILENO, &config) == 0) {
            config.c_lflag &= ~(ICANON | ECHO);
            config.c_cc[VMIN] = 1; /* Blocking input */
            config.c_cc[VTIME] = 0;
            tcsetattr(STDIN_FILENO, TCSANOW, &config);
            atexit(revert_terminal);
        }
    }

    /* Set standard input unbuffered. */
    setvbuf(stdin, NULL, _IONBF, 0);

    printf("Press Ctrl+C to exit.\n");
    fflush(stdout);

    while (!done) {
        c = fgetc(stdin);
        if (c == EOF)
            printf("Read EOF%s\n", ferror(stdin) ? " as an error occurred" : "");
        else
            printf("Read %d = 0x%02x\n", c, (unsigned int)c);
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

#define 行告诉您的 C 库头文件为基于 GNU 的系统公开 POSIX.1 功能。

每当接收到 INT (Ctrl+C)、TERM 或 HUP 信号时,都会设置 done 标志。 (如果您断开与终端的连接,例如关闭终端窗口,则会发送 HUP 信号。)

terminal_config 结构将包含原始终端设置,由注册为退出函数的revert_terminal() 使用,以将终端设置恢复为程序启动时读取的原始设置。

如果标准输入是终端,函数isatty(STDIN_FILENO) 返回 1。如果是这样,我们获取当前终端设置,并将它们修改为非规范模式,并要求每次读取都阻塞,直到至少读取一个字符。 (如果您设置了.c_cc[VMIN]=0.c_cc[VTIME]=0,I/O 函数往往会有点混乱,因此如果没有待处理的输入,fgetc() 返回 0。通常它看起来像 stdio.h I/O 的 EOF功能。)

接下来,我们告诉 C 库不要在内部缓冲标准输入,使用 setvbuf()。通常,C 库为标准输入使用输入缓冲区,以提高效率。但是,对我们来说,这意味着 C 库会缓冲输入的字符,而我们的程序在输入时可能不会立即看到它们。

同样,标准输出也被缓冲以提高效率。 C 库应该将所有完整的行刷新到实际的标准输出,但我们可以使用 fflush(stdout) 调用来确保我们写入 stdout 的所有内容都在此时刷新到实际的标准输出。

main() 中,我们有一个简单的循环,它读取按键,并以十进制和十六进制打印它们。

请注意,当传递信号时,例如 INT 信号,因为您键入了 Ctrl+C,将信号传递到我们的handle_done() 信号处理程序会中断fgetc() 呼叫(如果有待处理)。这就是为什么当您按下 Ctrl+C 时会看到 Read EOF 的原因;如果你之后检查ferror(stdin),你会看到它返回非零(这表明发生了错误)。本例中的“错误”是EINTR,“被信号中断”。

还请注意,当您按下某些特定键时,例如光标或功能键,您会看到生成多个字符,通常以 27 和 91 开头(十进制;0x1B 十六进制 0x5B;"\033[" 如果表示为 C 字符串文字)。这些通常但不总是 ANSI 转义序列。一般来说,它们是特定于终端的代码,可以使用terminfo 数据库通过tigetstr()tigetnum()tigetflag() 获得。


一种更便携的方法是使用 Curses 库;在大多数系统上是ncurses,在Windows 机器上是PDCurses。它们不仅提供了更简单的界面,而且还以特定于终端的方式来实现,以实现跨系统的最大兼容性。

使用 Curses 函数的 C 程序可以针对任何 Curses 库进行编译,因此可以在 Linux、Mac 和 Windows 机器上编译和运行相同的 C 源文件。但是,ncurses 确实包含相当多的扩展,这些扩展可能是其他 Curses 库(如 PDCurses)所不提供的。

【讨论】:

  • @chqrlie:意外! :) 现已修复;感谢您的提醒!
  • 感谢您在这里付出了很多努力来解释。但坦率地说,我是 c 编程的初学者,所以一旦我获得了更多关于 c 的知识,我会尝试理解您要解释的内容。再次感谢您
  • @JayPatel:这绝对没问题;我经常将我的“答案”写成更像是对那些有类似问题并对此感兴趣的人的背景或深入解释。对你来说,我想说我的答案简化为:使用 curses 库。如果您使用 Windows,请查看 here。否则使用 ncurses,使用操作系统提供的包管理;记得也要安装 ncurses 开发包。它们通常已经安装。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-08
  • 2012-06-04
相关资源
最近更新 更多