6

I am somewhat a beginner at Perl (compared to people here). I know enough to be able to write programs to do many things with through the command prompt. At one point, I decided to write a command prompt game that constructed a maze and let me solve it. Besides quality graphics, the only thing that it was missing was the ability for me to use the WASD controls without pressing enter after every move I made in the maze.

To make my game work, I want to be able to have Perl take a single character as input from STDIN, without requiring me to use something to separate my input, like the default \n. How would I accomplish this?

I have tried searching for a simple answer online and in a book that I have, but I didn't seem to find anything. I tried setting $/="", but that seemed to bypass all input. I think that there may be a really simple answer to my question, but I am also afraid that it might be impossible.

Also, does $/="" actually bypass input, or does it take input so quickly that it assumes there isn't any input if I'm not already pressing the key?

PhiNotPi
  • 163
  • 1
  • 5
  • 2
    [Term::ReadKey](http://search.cpan.org/perldoc?Term::ReadKey) – ikegami Dec 30 '11 at 06:08
  • Possible duplicate of [How do I read a single character from STDIN using Perl on Windows?](https://stackoverflow.com/questions/2021482/how-do-i-read-a-single-character-from-stdin-using-perl-on-windows) – Eugen Konkov Jul 08 '17 at 13:19

4 Answers4

4

IO::Prompt can be used:

#!/usr/bin/env perl

use strict;
use warnings;

use IO::Prompt;

my $key = prompt '', -1;
print "\nPressed key: $key\n";

Relevant excerpt from perldoc -v '$/' related to setting $/ = '':

The input record separator, newline by default. This influences Perl's idea of what a "line" is. Works like awk's RS variable, including treating empty lines as a terminator if set to the null string (an empty line cannot contain any spaces or tabs).

Alan Haggai Alavi
  • 72,802
  • 19
  • 102
  • 127
3

The shortest way to achieve your goal is to use this special construct:

$/ = \1;

This tells perl to read one character at a time. The next time you read from any stream (not just STDIN)

my $char = <STREAM>;

it will read 1 character per assignment. From perlvar "Setting $/ to a reference to an integer, scalar containing an integer, or scalar that's convertible to an integer will attempt to read records instead of lines, with the maximum record size being the referenced integer number of characters."

oᴉɹǝɥɔ
  • 1,796
  • 1
  • 18
  • 31
  • OP's question is about reading single keys interactively "*without requiring me to use something to separate my input, like the default `\n`*". That requires changing the terminal settings, which your code doesn't do. – melpomene Mar 23 '18 at 15:04
  • You're ... welcome? I mean, "*this answer is not useful*" is literally what that downvote button says. – melpomene Mar 23 '18 at 15:09
  • 2
    Does not exactly match the description, but answers the title... STDIN does not always come from a terminal, so +1 from me. – David L. Mar 19 '21 at 05:59
  • This may not match the body of the question, but it precisely answers the question in the title. The mismatch between the title and the body of the question is a different issue entirely. – William Pursell Feb 28 '23 at 15:12
1

If you are using *nix, you will find Curses useful.

It has a getch method that does what you want.

Term::TermKey also looks like a potential solution.

0

IO::Prompt is no longer maintained but IO::Prompter has a nice example (quoted from that site):

use IO::Prompter;
 
# This call has no automatically added options...
my $assent = prompt "Do you wish to take the test?", -yn;
 
{
    use IO::Prompter [-yesno, -single, -style=>'bold'];
 
    # These three calls all have: -yesno, -single, -style=>'bold' options
    my $ready = prompt 'Are you ready to begin?';
    my $prev  = prompt 'Have you taken this test before?';
    my $hints = prompt 'Do you want hints as we go?';
}
 
# This call has no automatically added options...
scalar prompt 'Type any key to start...', -single;
Jacob Wegelin
  • 1,304
  • 11
  • 16