如何从stdin中读取一个字符而不必输入?

ban*_*l23 24 rust

我想运行一个在stdin上阻塞的可执行文件,当按下一个键时,Enter不必按下即可立即打印相同的字符.

如何从stdin中读取一个字符而不必点击Enter?我从这个例子开始:

fn main() {
    println!("Type something!");

    let mut line = String::new();
    let input = std::io::stdin().read_line(&mut line).expect("Failed to read line");

    println!("{}", input);
}
Run Code Online (Sandbox Code Playgroud)

我通过API看起来并试图替换read_line()bytes(),但一切我尝试需要我打Enter的读取发生之前.

这个问题被要求用于C/C++,但似乎没有标准的方法:从标准输入中捕获字符而不等待按下输入

考虑到它在C/C++中并不简单,它在Rust中可能不可行.

doj*_*uba 13

虽然@ Jon使用ncurses的解决方案可行,但ncurses会按设计清除屏幕.我想出了这个解决方案,使用termios crate为我的小项目学习Rust.想法是通过访问termios绑定来修改ECHOICANON标记tcsetattr.

extern crate termios;
use std::io;
use std::io::Read;
use std::io::Write;
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};

fn main() {
    let stdin = 0; // couldn't get std::os::unix::io::FromRawFd to work 
                   // on /dev/stdin or /dev/tty
    let termios = Termios::from_fd(stdin).unwrap();
    let mut new_termios = termios.clone();  // make a mutable copy of termios 
                                            // that we will modify
    new_termios.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
    tcsetattr(stdin, TCSANOW, &mut new_termios).unwrap();
    let stdout = io::stdout();
    let mut reader = io::stdin();
    let mut buffer = [0;1];  // read exactly one byte
    print!("Hit a key! ");
    stdout.lock().flush().unwrap();
    reader.read_exact(&mut buffer).unwrap();
    println!("You have hit: {:?}", buffer);
    tcsetattr(stdin, TCSANOW, & termios).unwrap();  // reset the stdin to 
                                                    // original termios data
}
Run Code Online (Sandbox Code Playgroud)

读取单个字节的一个优点是捕获箭头键,ctrl等.不捕获扩展的F键(尽管ncurses可以捕获这些).

此解决方案适用于类UNIX平台.我没有使用Windows的经验,但根据这个论坛,也许SetConsoleMode在Windows中可以实现类似的东西.


小智 12

使用现在可用的"ncurses"库之一,例如这个库.

在Cargo中添加依赖项

[dependencies]
ncurses = "5.86.0"
Run Code Online (Sandbox Code Playgroud)

并包含在main.rs中:

extern crate ncurses;
use ncurses::*; // watch for globs
Run Code Online (Sandbox Code Playgroud)

按照库中的示例初始化ncurses并等待单个字符输入,如下所示:

initscr();
/* Print to the back buffer. */
printw("Hello, world!");

/* Update the screen. */
refresh();

/* Wait for a key press. */
getch();

/* Terminate ncurses. */
endwin();
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但似乎无法避免清除由“initscr()”强制执行的屏幕,如[此处](http://stackoverflow.com/questions/4772061/curses-library-c-getch-without -clearing-screen)和[那里](http://stackoverflow.com/questions/654471/ncurses-initialization-without-clearing-the-screen)。 (3认同)
  • 请注意,ncurses 仅适用于 Unix 系统,如果您需要 Unix 和 Windows 的跨平台支持,请使用 [pancurses](https://github.com/ihalila/pancurses)。 (3认同)

Ben*_*enC 7

您也可以使用termion,但您必须启用原始 TTY 模式,这也会改变其行为stdout。请参阅下面的示例(使用 Rust 1.34.0 测试)。请注意,在内部,它还包装了 termios UNIX API。

Cargo.toml

[dependencies]
termion = "1.5.2"
Run Code Online (Sandbox Code Playgroud)

主文件

[dependencies]
termion = "1.5.2"
Run Code Online (Sandbox Code Playgroud)