Perl 的 -0 选项到底是如何工作的?

Perl 的 -0 选项到底是如何工作的?

根据man perlrun

-0[octal/hexadecimal]
     specifies the input record separator ($/) as an octal or
     hexadecimal number. If there are no digits, the null character is
     the separator. 

The special value 00 will cause Perl to slurp files in paragraph
mode.  Any value 0400 or above will cause Perl to slurp files
whole, but by convention the value 0777 is the one normally used
for this purpose.

然而,给定这个输入文件:

This is paragraph one

This is paragraph two.

我得到了一些意想不到的结果:

$ perl -0ne 'print; exit' file ## \0 is used, so everything is printed
This is paragraph one.

This is paragraph two.

 $ perl -00ne 'print; exit' file ## Paragraph mode, as expected
 This is paragraph one.

到目前为止,一切都很好。现在,为什么这两个似乎也可以在段落模式下工作?

$ perl -000ne 'print; exit' file 
This is paragraph one.

$ perl -0000ne 'print; exit' file 
This is paragraph one.

为什么这个显然又吞食了整个文件?

$ perl -00000ne 'print; exit' file 
This is paragraph one.

This is paragraph two.

进一步的测试表明,这些似乎都可以在段落模式下工作:

perl -000 
perl -0000
perl -000000
perl -0000000
perl -00000000

虽然这些似乎吞噬了整个文件:

perl -00000
perl -000000000

我想我的问题是我对八进制的理解不够好(真的),我是一名生物学家,而不是程序员。后两者是否会吞掉整个文件,因为000000000000都是>= 0400?或者发生了完全不同的事情?

答案1

八进制就像十进制一样,0 == 0、0000 == 0、0 == 000000 等。事实上,这里的开关-0可能会让事情有点混乱——我会假设关于“特殊值”的观点00”表示一个0为开关,一个为数值;添加更多的零不会改变后者,所以你会得到同样的结果......

在一定程度上。等的行为000000有点像错误,但请记住,这应该指的是单个 8 位值。 8 位的范围,十进制为 0-255,八进制为 0-377。因此,您不可能在这里使用超过 3 位数字(特殊值均超出该范围,但仍然是 3 位数字 + 开关)。您可能只是从以下内容中推断出这一点:

您还可以使用十六进制表示法指定分隔符:-0xHHH...,其中 H 是有效的十六进制数字。与八进制形式不同,这个可以用来指定任何 Unicode 字符,即使那些超出 0xFF 的值

0xFF 十六进制 == 255 十进制 == 377 八进制 == 8 位最大值,即一个字节的大小和(扩展)ASCII 集中的一个字符。

答案2

让我们查看perl源代码以了解更多详细信息。在perl.c:

case '0':
    {
     I32 flags = 0;
     STRLEN numlen;

     SvREFCNT_dec(PL_rs);
     if (s[1] == 'x' && s[2]) {
          const char *e = s+=2;
          U8 *tmps;

          while (*e)
        e++;
          numlen = e - s;
          flags = PERL_SCAN_SILENT_ILLDIGIT;
          rschar = (U32)grok_hex(s, &numlen, &flags, NULL);
          if (s + numlen < e) {
           rschar = 0; /* Grandfather -0xFOO as -0 -xFOO. */
           numlen = 0;
           s--;
          }
          PL_rs = newSVpvs("");
          SvGROW(PL_rs, (STRLEN)(UNISKIP(rschar) + 1));
          tmps = (U8*)SvPVX(PL_rs);
          uvchr_to_utf8(tmps, rschar);
          SvCUR_set(PL_rs, UNISKIP(rschar));
          SvUTF8_on(PL_rs);
     }
     else {
          numlen = 4;
          rschar = (U32)grok_oct(s, &numlen, &flags, NULL);
          if (rschar & ~((U8)~0))
           PL_rs = &PL_sv_undef;
          else if (!rschar && numlen >= 2)
           PL_rs = newSVpvs("");
          else {
           char ch = (char)rschar;
           PL_rs = newSVpvn(&ch, 1);
          }
     }
     sv_setsv(get_sv("/", GV_ADD), PL_rs);
     return s + numlen;
    }

格罗克_八月将表示八进制数的字符串转换为数字形式。如果尝试输入无效的八进制数字,它会立即返回。它只假设每 4 个字符 (numlen = 4) 为一个有效值(您可以在其实现中看到 for 循环)数字.c

所以在 中-00000,首先perl解析-0000并设置$/\000.最后一个0被认为是perl -0,导致再次$/设置\000。您可以在以下位置看到:

$ perl -MO=Deparse -00000777ne 'print; exit' file
BEGIN { $/ = undef; $\ = undef; }
LINE: while (defined($_ = <ARGV>)) {
    print $_;
    exit;
}
-e syntax OK

$/被设置为undef,因为最后解析的八进制序列perl0777

更清楚:

$ perl -MO=Deparse -00000x1FF -ne 'print; exit' file
BEGIN { $/ = "\x{1ff}"; $\ = undef; }
LINE: while (defined($_ = <ARGV>)) {
    print $_;
    exit;
}
-e syntax OK

您可以看到$/已设置为最后 4 位数字序列0x1FF

相关内容