如何在不按回车键的情况下获取单个字符?

46

如何在 Ruby 中从终端获取单个按键字符而无需按回车键? 我尝试使用 Curses::getch,但对我来说并没有真正起作用。


2
可能是从控制台立即获取单个字符的重复问题。 - Phrogz
6个回答

72

自Ruby 2.0.0起,stdlib中有一个具有此功能的“io / console”

require 'io/console'
STDIN.getch

文档链接:http://ruby-doc.org/stdlib/libdoc/io/console/rdoc/IO.html#method-i-getch - Simon Perepelitsa
13
如果你之后检查了控制-C,那么这个方法就很好用:exit(1) if char == "\u0003" - Aidan Feldman
我甚至不需要 require :) - Lucas Caton
@philant 不,我有一个脚本,通过 $ ruby script.rb 调用它可以工作。 - Lucas Caton
这可能是目前最好的答案,因为如果需要更改功能,则Ruby核心团队(例如nobu)可以修改/调整它。在我看来,从长远来看,这应该会更好。 - shevy

37

到目前为止,它可以工作,但不幸的是我得到了一个数字而不是字符串。您知道如何将数字转换为正确的ASCII字符吗? - Nino
2
是的,只需使用str.chr来获得与数字值相对应的字符。我已更新帖子以反映这一点。 - Jay
1
不幸的是,由于您处于原始模式,因此控制-C 会作为字符发送,而不是作为 SIGINT 发送。因此,如果您想要像上面那样进行阻塞输入,但仍希望让用户在等待程序时按下控制-C 停止程序,请确保执行以下操作: Signal.trap("INT") { exit } (有关更好格式化版本,请参见下面的答案) - AlexChaffee
2
以下是Andrew(于2013年1月25日17:49)的回答更好。 - Will

21

@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

15

使用原始模式(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仅在短暂的时间内处于原始模式。


这个答案非常棒,正是我一直在找的。非常感谢你提供如此出色的代码! - eddieroger
1
Signal.trap('INT') { exit } 当按下 Ctrl-C 时使程序退出(除非通过其他方式绕过),并且在1.9.2-p290中肯定有效,因此我不知道“对我无效”可能意味着什么。 - AlexChaffee
这在你的示例中可以工作,但如果你使用STDIN.getch,它绝对不起作用。然后你的SIGINT被解释为"\u0003"。或者根据你的终端可能是其他内容。 - Justin Force

13

注意:这是一份旧的答案,大多数系统上该解决方案已不再适用。

但在其他方法无法生效的某些环境中,该答案仍可能有用。请阅读下面的评论。


首先,您需要安装highline:

gem install highline

然后尝试一下高级查询方法是否适合您:

require "highline/system_extensions"
include HighLine::SystemExtensions

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

5
警告:最近的提交从highline中移除了这个功能。请参见https://github.com/JEG2/highline/issues/50。 - AlexChaffee
2
这个不起作用:它需要你按下<enter>键。顺便说一句,它还接受\n字符。 - erapert
这个示例在我使用的Windows 7上的Ruby 2.0上运行良好。 - zhon
可以在Ruby 1.9.3和Windows XP上工作,无需按Enter键(在pry上测试过)。感谢! - Darek Nędza

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接