2014-10-12 20 views
15

Tôi muốn chạy tệp thực thi chặn trên stdin và khi nhấn một phím mà cùng một ký tự được in ngay lập tức mà không cần Nhập phải được nhấn.Làm cách nào để đọc một ký tự từ stdin mà không phải nhấn enter?

Làm cách nào để đọc một ký tự từ stdin mà không phải nhấn Nhập? Tôi bắt đầu với ví dụ này:

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); 
} 

Tôi nhìn qua API và cố gắng thay thế read_line() với bytes(), nhưng tất cả những gì tôi cố gắng đòi hỏi tôi phải nhấn Nhập trước khi đọc xảy ra.

Câu hỏi này được hỏi cho C/C++, nhưng có vẻ là không có cách nào tiêu chuẩn để làm điều đó: Capture characters from standard input without waiting for enter to be pressed

Nó có thể không được doable trong Rust coi đó là không đơn giản trong C/C++.

+3

Đây là vấn đề nền tảng chứ không phải ngôn ngữ. Trên các cửa sổ có các chức năng nhập ký tự, nhưng trên unix/linux, bạn phải thực hiện thiết bị đầu cuối ngoài chế độ đệm dòng. –

+0

Bạn có thể sử dụng hàm 'getch' từ liên kết SO đã đề cập. Bạn chỉ cần biên dịch nó thành một đối tượng chia sẻ và sử dụng nó từ Rust: https://gist.github.com/ihrwein/a4558d63d9250ee0bbf6 Bạn sẽ cần một trình biên dịch C và nó chỉ hoạt động trên Linux (ít nhất là tôi đã thử nghiệm nó ở đó) . –

Trả lời

10

Sử dụng một trong các thư viện 'ncurses' hiện có sẵn, ví dụ: this một.

Thêm sự phụ thuộc vào hàng hóa

[dependencies] 
ncurses = "5.86.0" 

và đưa vào main.rs:

extern crate ncurses; 
use ncurses::*; // watch for globs 

Thực hiện theo các ví dụ trong thư viện để khởi ncurses và chờ cho đầu vào ký tự đơn như thế này:

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

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

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

/* Terminate ncurses. */ 
endwin(); 
+0

Công trình này, nhưng có vẻ như không thể tránh việc xóa màn hình được thực thi bởi 'initscr()' như được thảo luận [ở đây] (http://stackoverflow.com/questions/4772061/curses-library-c-getch- mà không cần xóa màn hình) và [ở đó] (http://stackoverflow.com/questions/654471/ncurses-initialization-without-clearing-the-screen). – dojuba

9

Trong khi giải pháp @ Jon sử dụng công cụ ncurses, ncurses xóa màn hình theo thiết kế. Tôi đã đưa ra giải pháp này sử dụng termios crate cho dự án nhỏ của tôi để tìm hiểu Rust. Ý tưởng là sửa đổi các cờ ECHOICANON bằng cách truy cập tcsetattr thông qua các liên kết từ.

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 
} 

Một lợi thế của việc đọc một byte đơn là chụp các phím mũi tên, ctrl, vv. Phím F mở rộng không được chụp (mặc dù ncurses có thể nắm bắt các).

Giải pháp này dành cho nền tảng giống UNIX. Tôi không có kinh nghiệm với Windows, nhưng theo điều này forum có lẽ một cái gì đó tương tự có thể đạt được bằng cách sử dụng SetConsoleMode trong Windows.

Các vấn đề liên quan