Perl - странное поведение цикла с ReadKey - PullRequest
1 голос
/ 08 ноября 2011

По какой-то причине в моей простой программе меню странные вещи происходят в результате ReadKey ()

#!/usr/local/bin/perl

use strict ;
use warnings ;
use English ;
use Term::ReadKey ;

my @available_choices = ('choice one', 'choice two', 'choice three') ;
my $array_size = scalar (@available_choices) ;

print "\nPlease make your selection from the options below:\n\n" ;

for (my $i=0, my $j=1 ; $i < $array_size ; $i++, $j++) {
  print "$j) $available_choices[$i]\n" ;
}

my $key = undef ;
for (my $k=0; $k < 5; $k++) {
  print "\nSelection :> " ;
  $key = ReadKey();
  if ((defined $key) && ($key =~ /[1-$array_size]/)) {
    print "\nYou selected \"$available_choices[$key-1]\"\n" ;
    last ;
  }
  else {
    sleep 1 ;
  }
}

Так что если вы запустите эту простую программу и дадите 1, 2 или 3 в качестве вашего выбораэто работает как ожидалось.Если вы введете что-то еще (чтобы вызвать блок else), цикл повторяется 3 или 4 раза, прежде чем ReadKey () снова принимает ввод.Лучше всего это иллюстрирует этот вывод (я ввел xxx, а затем напечатал «Selection:>» 3 раза, прежде чем смог набрать yyy):

$ ./bar.pl 

Please make your selection from the options below:

1) choice one
2) choice two
3) choice three

Selection :> xxx

Selection :> 
Selection :> 
Selection :> 
Selection :> yyy

Ответы [ 2 ]

4 голосов
/ 08 ноября 2011

Это потому, что ReadKey читает ключ . Когда вы нажимаете x 3 раза, а затем вводите , это 4 клавиши. На самом деле, даже правильный выбор ( 1 Enter ) составляет 2 клавиши; вы просто не замечаете, потому что ваша программа завершается немедленно.

Это менее очевидно, потому что режим ввода по умолчанию буферизует нажатия клавиш до тех пор, пока вы не нажмете Enter . В этот момент ReadKey начнет возвращать каждое нажатие клавиши по одному.

Решение зависит от поведения, которое вы ищете. Если вы хотите нажать Enter , прежде чем воздействовать на вход, вы можете просто прочитать строку за раз (с помощью стандартного оператора <>). Вам вообще не нужно Term::ReadKey.

Если вы хотите выполнить действие сразу после нажатия клавиши, вам нужно использовать функцию Term::ReadKey ReadMode, чтобы изменить буферизацию ввода. Не забудьте добавить END { ReadMode(0) } для восстановления исходного режима при выходе из программы.

1 голос
/ 08 ноября 2011

Похоже, ваша программа читает ключ, входит в цикл else и спит, пока вы набираете другие символы, а затем продолжает.Возможно, вы страдаете от буферизации, потому что печать происходит не сразу при использовании сна.

Ваша программа может использовать некоторые альтернативные методы.Вот пример:

use strict;
use warnings;
use v5.10;

my @available_choices = ('choice one', 'choice two', 'choice three') ;

print "\nPlease make your selection from the options below:\n\n" ;

# Using a block to reduce scope of $i
# No need to use a separate variable for the array length
{ 
    my $i = 0;
    say ++$i, ") $_" for @available_choices;
}

# No need to use a temp variable for a loop
# Also, might be better off with an infinite loop, 
# rather than one with 5 cycles.

#for (0 .. 4) {
while (1) {
    print "\nSelection :> " ;
    chomp(my $key = <>);     # Using STDIN instead of ReadKey
    last if $key =~ /^q$/i;  # easy exit
    next unless $key =~ /^\d+$/;
    if (defined $available_choices[$key-1]) {
        say qq(\nYou selected "$available_choices[$key-1]"\n);
        last;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...