Как получить один символ без нажатия клавиши ввода?

Как я могу получить один символ клавиатуры из терминала с помощью Ruby, не нажимая Enter? Я старался Curses::getch, но это не сработало для меня.

6 ответов

Решение

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/2999

#!/usr/bin/ruby

begin
  system("stty raw -echo")
  str = STDIN.getc
ensure
  system("stty -raw echo")
end
p str.chr

(Протестировано на моей системе OS X, возможно, не переносимо на все платформы Ruby). Смотрите http://www.rubyquiz.com/quiz5.html для некоторых дополнительных предложений, в том числе для Windows.

Начиная с ruby ​​2.0.0, в stdlib есть 'io/console' с этой функцией

require 'io/console'
STDIN.getch

@Джей дал отличный ответ, но есть две проблемы:

  1. Вы можете испортить состояние tty по умолчанию;
  2. Вы игнорируете управляющие символы (^C для SIGINT и т. Д.).

Простым решением для этого является сохранение предыдущего состояния tty и использование следующих параметров:

  • -icanon - отключить канонический ввод (обработка ERASE и KILL);
  • isig - включить проверку символов по специальным управляющим символам INTR, QUIT и SUSP.

В конце у вас будет такая функция:

def get_char
  state = `stty -g`
  `stty raw -echo -icanon isig`

  STDIN.getc.chr
ensure
  `stty #{state}`
end

Необработанный режим (stty raw -echo) к сожалению заставляет control-C отправляться как символ, а не как SIGINT. Поэтому, если вы хотите заблокировать ввод, как описано выше, но разрешить пользователю нажимать control-C, чтобы остановить программу, пока она ждет, убедитесь, что сделали это:

Signal.trap("INT") do # SIGINT = control-C
  exit
end

И если вы хотите неблокирующий ввод - то есть периодически проверяйте, нажал ли пользователь клавишу, а тем временем переходите к другим действиям - тогда вы можете сделать это:

require 'io/wait'

def char_if_pressed
  begin
    system("stty raw -echo") # turn raw input on
    c = nil
    if $stdin.ready?
      c = $stdin.getc
    end
    c.chr if c
  ensure
    system "stty -raw echo" # turn raw input off
  end
end

while true
  c = char_if_pressed
  puts "[#{c}]" if c
  sleep 1
  puts "tick"
end

Обратите внимание, что вам не нужен специальный обработчик SIGINT для неблокирующей версии, так как tty находится в режиме raw только на короткое время.

Примечание. Это старый ответ, и решение больше не работает на большинстве систем.

Но ответ все еще может быть полезен для некоторых сред, где другие методы не работают. Пожалуйста, прочитайте комментарии ниже.


Сначала вы должны установить Highline:

gem install highline

Затем попробуйте, если метод highline работает для вас:

require "highline/system_extensions"
include HighLine::SystemExtensions

print "Press any key:"
k = get_character
puts k.chr

И если вы создаете приложение curses, вам нужно позвонить

nocbreak

http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html

Другие вопросы по тегам