C语言 使用标准输入识别箭头键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4130048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:57:10  来源:igfitidea点击:

Recognizing arrow keys with stdin

cinputterminalocamlstdin

提问by Hyman

is it possible to have a cross-platform way to handle backspace and arrows keys within a C or OCaml program?

是否有可能在 C 或 OCaml 程序中使用跨平台的方式来处理退格键和箭头键?

Actually an OCaml solution would be appreciated but many standard unix functions are wrapped directly to corresponding API calls so there's should be no problem in porting a C solution.

实际上,OCaml 解决方案会受到赞赏,但许多标准的 unix 函数都直接包装到相应的 API 调用中,因此移植 C 解决方案应该没有问题。

What I'm going to achieve is to catch the arrow keys to override its behaviour inside the shell (by repropting last line or operations like these). I think that this thing falls before the actual program and it's not handled by code itself so I don't know if it's possible.

我要实现的是捕捉箭头键来覆盖它在外壳内的行为(通过重新传播最后一行或这样的操作)。我认为这件事发生在实际程序之前,它不是由代码本身处理的,所以我不知道是否可能。

The program is compiled either on Linux, OS X and Windows (on cygwin) so I would like to do it for all platforms..

该程序是在 Linux、OS X 和 Windows(在 cygwin 上)上编译的,所以我想在所有平台上都这样做。

采纳答案by Niki Yoshiuchi

I've done something pretty similar recently (although my code is Linux only). You have to set stdin to non-canonical mode in order to read arrow key presses. This should work on OS X and Linux and will probably work on Cygwin although I can't say for sure.

我最近做了一些非常相似的事情(虽然我的代码只是 Linux)。您必须将 stdin 设置为非规范模式才能读取箭头键按下。这应该适用于 OS X 和 Linux,并且可能适用于 Cygwin,尽管我不能肯定地说。

open Unix
let terminfo = tcgetattr stdin in
  let newterminfo = {terminfo with c_icanon = false; c_vmin = 0; c_vtime = 0} in
    at_exit (fun _ -> tcsetattr stdin TCSAFLUSH terminfo); (* reset stdin when you quit*)
    tcsetattr stdin TCSAFLUSH newterminfo;

when canonical mode is off, you don't need to wait for a newline in order to read from stdin. c_vmin represents the minimum numbers of characters to read before returning (you probably want to be able to read a single character at a time) and c_vtime is the maximum read wait time (in 0.1s units).

当规范模式关闭时,您无需等待换行符即可从标准输入读取。c_vmin 表示返回之前要读取的最小字符数(您可能希望一次读取一个字符),而 c_vtime 是最大读取等待时间(以 0.1 秒为单位)。

You might also want to set c_echoto false so that the arrow key presses are printed to the terminal (but then you'll have to manually print everything else.

您可能还想设置c_echo为 false 以便将箭头键按下打印到终端(但是您必须手动打印其他所有内容。

Most terminals represent arrow key presses using ANSI escape sequences. If you run catwith no arguments and start hitting the arrow keys you can see the escape sequences used. They are typically

大多数终端使用ANSI 转义序列表示箭头键按下。如果您cat不带参数运行并开始按箭头键,您可以看到使用的转义序列。他们通常是

up - "3[A"
down - "3[B"
left - "3[D"
right - "3[C"

Where '\033' is the ascii value for esc

其中 '\033' 是 ascii 值 esc

回答by Ignacio Vazquez-Abrams

Use ncurses to extract the sequences for the arrow key capabilities and then look for them when you read stdin. It's probably easier to use something like libedit or readline instead, and let that deal with the keyhandling.

使用 ncurses 提取箭头键功能的序列,然后在读取 stdin 时查找它们。使用 libedit 或 readline 之类的东西可能更容易,并让它处理键处理。

回答by Gilles 'SO- stop being evil'

The standard way of supporting keyboard input beyond lines of printable characters is through the ncurseslibrary, which has an Ocaml binding. Another common possibility is the readlinelibrary (most famously used by Bash).

支持超出可打印字符行的键盘输入的标准方法是通过ncurses库,该库具有Ocaml 绑定。另一种常见的可能性是readline库(最著名的是 Bash 使用的)。

If all you're doing is reading input line by line, but want your users to have a decent line editor, there's no need to include any support in your program. Instead, just tell your users to use a wrapper program such as rlwrap(which is based on readline) or ledit. These wrappers do provide line edition and history, the two features you list as your requirements. I would recommend that you build input processing into your program only if you want something fancier, such as program-specific completion when the user presses Tab.

如果您所做的只是逐行读取输入,但希望您的用户拥有一个不错的行编辑器,则无需在您的程序中包含任何支持。相反,只需告诉您的用户使用包装程序,例如rlwrap(基于 readline)或ledit。这些包装器确实提供了行版本和历史记录,这是您作为要求列出的两个功能。我建议您仅在您想要更高级的东西时才将输入处理构建到您的程序中,例如当用户按下时特定于程序的完成Tab

回答by Clifford

Backspace is an ASCII character and will be placed in stdin like any other character. The character escape sequence '\b'is the backspace character.

Backspace 是一个 ASCII 字符,将像任何其他字符一样放置在 stdin 中。字符转义序列'\b'是退格字符。

For cursor keys, these are not associated with any control character, so do not generate data in the stdin stream. Low level access is necessarily platform specific, although there are cross-platform libraries that abstract away the platform differences. I believe ncurses is available for all the platforms you mentioned.

对于光标键,它们不与任何控制字符相关联,因此不要在 stdin 流中生成数据。低级访问必然是特定于平台的,尽管存在抽象出平台差异的跨平台库。我相信 ncurses 适用于您提到的所有平台。