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

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

возможный дубликат Немедленно получить одиночный символ из консоли

Phrogz 16.11.2011 01:05
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
44
1
26 086
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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.

Пока это работает, но, к сожалению, я получаю число, а не строку. вы знаете, как преобразовать число в правильный символ ascii?

Nino 06.10.2008 20:33

Да, просто используйте str.chr, чтобы получить символ, соответствующий числовому значению. Я обновил сообщение, чтобы отразить это

Jay 06.10.2008 20:50

К сожалению, поскольку вы находитесь в необработанном режиме, control-C отправляется как символ, а не как SIGINT. Поэтому, если вы хотите заблокировать ввод, как указано выше, но все же хотите, чтобы пользователь нажимал Ctrl-C, чтобы остановить программу во время ожидания, обязательно сделайте это: Signal.trap("INT") { exit } (см. Мой ответ ниже для версии с лучшим форматом)

AlexChaffee 26.11.2011 01:10

Ответ Андрея (25 января 2013, 17:49) лучше.

Will 04.11.2014 00:35

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

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


Для начала нужно установить highline:

gem install highline

Тогда попробуйте, подходит ли вам метод highline:

require "highline/system_extensions"
include HighLine::SystemExtensions

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

Предупреждение: недавняя фиксация удалила эту функцию из highline. См. github.com/JEG2/highline/issues/50

AlexChaffee 25.01.2013 22:53

это НЕ работает: для этого нужно нажать клавишу <enter>. Кстати, он также принимает символ \ n.

erapert 09.05.2013 02:16

Этот пример у меня работает на Ruby 2.0 в Windows 7.

zhon 18.12.2013 23:18

Работает на Ruby 1.9.3 и Windows XP без необходимости нажимать клавишу ввода (проверено на pry). ps Спасибо!

Darek Nędza 30.01.2014 22:37

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

Отличный ответ, именно то, что я искал. Большое спасибо за исключительно функциональный код!

eddieroger 29.11.2012 23:29

Signal.trap('INT') { exit } выполняет выход из программы при нажатии Ctrl-C (если это не обходится другими способами) и определенно работает в 1.9.2-p290, поэтому я не знаю, что могло означать «не сработало для меня»

AlexChaffee 20.01.2013 02:23

Это работает в вашем примере, но определенно не работает, если вы используете STDIN.getch. Тогда ваш SIGINT интерпретируется как "\u0003". Или, может быть, что-то еще в зависимости от вашего терминала.

Justin Force 18.11.2015 04:11

А если вы собираете приложение проклятия, вам нужно вызвать

nocbreak

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

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

  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

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

require 'io/console'
STDIN.getch

Ссылка на документацию: ruby-doc.org/stdlib/libdoc/io/console/rdoc/…

Simon Perepelitsa 12.05.2015 11:00

Это сработало отлично, при условии, что вы потом проверите Control-C: exit(1) if char == "\u0003".

Aidan Feldman 02.08.2015 01:31

Мне даже require не понадобился :)

Lucas Caton 06.09.2017 03:26

@philant nope, у меня есть скрипт, и вызов его через $ ruby script.rb работает.

Lucas Caton 25.09.2017 01:22

Это, вероятно, лучший ответ на сегодняшний день, потому что, если функциональность должна быть изменена, команда ядра Ruby (например, nobu) может изменить / адаптировать ее. Это должно быть лучше в долгосрочной перспективе, ИМО.

shevy 04.02.2021 11:10

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