Perl 从标准输入输入一个字符

Perl do input one char from stdin

Perl 如何从 stdin 输入一个字符,例如

readline -N1

是吗?

您可以使用基本的 perl 发行版来做到这一点,无需安装额外的软件包:

use strict;
sub IO::Handle::icanon {
        my ($fh, $on) = @_;
        use POSIX;
        my $ts = new POSIX::Termios;
        $ts->getattr(fileno $fh) or die "tcgetattr: $!";
        my $f = $ts->getlflag;
        $ts->setlflag($on ? $f | ICANON : $f & ~ICANON);
        $ts->setattr(fileno $fh) or die "tcsetattr: $!";
}

# usage example
# a key like `Left` or `á` may generate multiple bytes
STDIN->icanon(0);
sysread STDIN, my $c, 256;
STDIN->icanon(1);
# the read key is in $c

只读取一个字节可能不是一个好主意,因为它只会留下垃圾,以便稍后在按 LeftF1 等键时读取。但是,如果您想要的话,您可以将 256 替换为 1,无论如何。

<STDIN>会读stdin一个byte(Cchar类型,跟一个character[=98不一样=] 如果记录分隔符设置为对数字 1 的引用,这些天通常由几个字节组成,除了 US-ASCII 字符集中的那些)。

$ echo perl | perl -le '$/ = ; $a = <STDIN>; print "<$a>"'
<p>

请注意,在下面,它可能会从输入中读取(消耗)多个字节。上面,perl 中的下一个 <STDIN> 将是 return <e>,但可能来自事先读取的某个大缓冲区。

$ echo perl | (perl -le '$/ = ; $a = <STDIN>; print "<$a>"'; wc -c)
<p>
0

在上面,您会注意到 wc 没有收到任何输入,因为它已经被 perl 使用了。

$ echo perl | (PERLIO=raw perl -le '$/ = ; $a = <STDIN>; print "<$a>"'; wc -c)
<p>
4

这一次,wc 得到了 4 个字节(erl\n),正如我们告诉 perl使用原始 I/O 所以 <STDIN> 转换为 read(0, bud, 1).

您可以使用 perlread 而不是 <STDIN>,但注意相同:

$ echo perl | (perl -le 'read STDIN, $a, 1; print "<$a>"'; wc -c)
<p>
0
$ echo perl | (PERLIO=raw perl -le 'read STDIN, $a, 1; print "<$a>"'; wc -c)
<p>
4

或者使用 sysread 这是原始 read():

的真正包装器
$ echo perl | (perl -le 'sysread STDIN, $a, 1; print "<$a>"'; wc -c)
<p>
4

要一次读取一个字符,您需要一次读取一个字节,直到字符结束。

您可以在 perl 中使用 <STDIN>read(不是 sysread)对 UTF-8 编码输入(在使用该编码的语言环境中)执行此操作-C 选项,包括 raw PERLIO:

$ echo été | (PERLIO=raw perl -C -le '$/ = ; $a = <STDIN>; print "<$a>"'; wc -c)
<é>
4
$ echo été | (PERLIO=raw perl -C -le 'read STDIN, $a, 1; print "<$a>"'; wc -c)
<é>
4

使用 strace,您会看到 perl 在下面执行两个 read(0, buf, 1) 系统调用来读取那个 2 字节的 é 字符。

与 ksh93 / bash 的 read -N(或 zsh 的 read -k)一样,如果输入未正确编码为 UTF-8,您可能会大吃一惊:

$ printf '5 12345678' | (PERLIO=raw perl -C -le 'read STDIN, $a, 1; print "<$a>"'; wc -c)
<� 1234>
4

5 (\xFD) 通常是 UTF-8 中 6 字节字符编码的第一个字节,因此 perl 在这里读取所有 6 个字节,即使第二到第六不可能是该字符的一部分,因为它们没有设置第 8th 位。

注意,当 stdin 是 tty 设备时,read() 不会 return 直到另一端的终端发送 LF(eol),CR(默认情况下转换为 LF),或 eof(通常为 ^D)或 eol2(通常未定义)字符,如在 tty 线路规程中配置的(与 stty 命令一样)为tty 驱动程序实现了它自己的内部行编辑器,允许您在按回车键之前编辑您键入的内容。

如果您想读取为用户按下的每个键发送的字节,您需要禁用该行编辑器(bash/ksh93 的 read -Nzshread -k 在 stdin 是 tty 时执行),请参阅 @guest's answer 了解如何执行此操作的详细信息。


¹ 虽然现在 Unicode 将代码点限制为最多 0x10FFFF,这意味着 UTF-8 编码最多有 4 个字节,但 UTF-8 最初设计用于编码最多 0x7fffffff(最多 6 个字节)的代码点编码)和 perl 将其扩展到最多 0x7FFFFFFFFFFFFFFFF(13 字节编码)