Como exatamente funciona a opção -0 de perl?

6

De acordo com 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. 

e

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.

No entanto, dado este arquivo de entrada:

This is paragraph one

This is paragraph two.

Recebo alguns resultados inesperados:

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

$ perl -0000ne 'print; exit' file 
This is paragraph one.
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.

Até agora, tudo bem. Agora, por que esses dois parecem funcionar também no modo de parágrafo?

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

This is paragraph two.

E por que esse vídeo está aparentemente sugando o arquivo inteiro de novo?

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

Outros testes mostram que todos parecem funcionar no modo de parágrafo:

perl -00000
perl -000000000

Embora pareçam ingerir o arquivo inteiro:

-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. 

Eu acho que o meu problema é que eu não entendo o octal bem o suficiente (na verdade, na verdade), eu sou um biólogo, não um programador. Será que os dois últimos sugiram o arquivo inteiro porque 0000 e 00000000 são >= 0400 ? Ou há algo completamente diferente acontecendo?

    
por terdon 25.03.2015 / 19:48

2 respostas

4

Octal é igual a decimal em que 0 == 0, e 0000 == 0, 0 == 000000, etc. O fato de que a troca aqui é -0 pode tornar as coisas um pouco confusas - eu presumo ponto sobre "o valor especial 00" significa um 0 para o switch e um para o valor; adicionar mais zeros não vai mudar o último, então você obtém a mesma coisa ...

Até certo ponto. O comportamento de 000000 etc. é parecido com um bug, mas tenha em mente que isso deve se referir a um único valor de 8 bits . O intervalo de 8 bits em decimal é 0-255, em octal, 0-377. Então você não pode usar mais de 3 dígitos aqui de forma significativa (os valores especiais estão todos fora desse intervalo, mas ainda 3 dígitos + o switch). Você talvez queira inferir isso de:

You can also specify the separator character using hexadecimal notation: -0xHHH..., where the H are valid hexadecimal digits. Unlike the octal form, this one may be used to specify any Unicode character, even those beyond 0xFF.

0xFF hex == 255 decimal == 377 octal == max para 8 bits, o tamanho de um byte e um caractere no conjunto ASCII (estendido).

    
por 25.03.2015 / 20:07
3

Vamos analisar a perl source para mais detalhes. Em 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;
    }

grok_oct converte uma string representando um número octal para o formato numérico. Ele retorna imediatamente se tentar um dígito octal inválido. E só assume cada 4 caracteres (numlen = 4) para um valor válido (Você pode ver o loop for em sua implementação em numeric.c )

Então, em -00000 , primeiro perl parse -0000 e defina $/ to 00 . O último perl -0 é considerado como $/ , fazendo com que $/0 seja definido novamente como undef . Você pode ver em:

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

perl foi definido como 0777 , porque a última sequência octal $/ parsed é 0x1FF .

Mais claramente:

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

Você pode ver %code% definido como a última seqüência de 4 dígitos %code% .

    
por 25.03.2015 / 21:27

Tags