Pregunta

I generalmente bucle a través de líneas en un archivo utilizando el código siguiente:

open my $fh, '<', $file or die "Could not open file $file for reading: $!\n";
while ( my $line = <$fh> ) {
  ...
}

Sin embargo, para responder a otra pregunta , Evan Carroll editado mi respuesta, cambiar mi declaración while a:

while ( defined( my $line = <$fh> ) ) {
  ...
}

Su razonamiento era que si usted tiene una línea que es 0 (que tendría que ser la última línea, de lo contrario tendría un retorno de carro), entonces su while podría salir antes de tiempo si se ha utilizado mi declaración ($line se establece en "0", y el valor de retorno de la asignación serían por lo tanto también ser "0" que consigue evaluada a falso). Si marca para-dad definida, entonces no se encuentra con este problema. Tiene perfecto sentido.

Así que lo intentó. He creado un archivo de texto cuya última línea es 0 sin retorno de carro en él. Lo pasé por mi bucle y el bucle no ha salido antes de tiempo.

luego pensé, "Aha, tal vez el valor no es en realidad 0, tal vez hay algo más allí que de arruinar las cosas!" Así que utilicé Dump() de Devel::Peek y esto es lo que me dio:

SV = PV(0x635088) at 0x92f0e8
  REFCNT = 1
  FLAGS = (PADMY,POK,pPOK)
  PV = 0X962600 "0"\0
  CUR = 1
  LEN = 80

Esto parece decirme que el valor es en realidad el "0" cadena, como llegue a un resultado similar si llamo Dump() en un escalar que he establecido explícitamente a "0" (la única diferencia es en el campo LEN - Del archivo LEN es 80, mientras que desde el escalar LEN es 8).

Entonces, ¿cuál es el problema? ¿Por qué no mi while() salida de bucle lo hace antes de tiempo si lo paso una línea que sólo "0" sin retorno de carro? Es bucle de Evan en realidad más defensivo, o hace Perl hacer algo loco internamente, que significa que no hay que preocuparse por estas cosas y while() en realidad sólo lo hace la salida cuando se pulse eof?

¿Fue útil?

Solución

Debido

 while (my $line = <$fh>) { ... }

En realidad compila hasta

 while (defined( my $line = <$fh> ) ) { ... }

Puede que haya sido necesario en una versión muy antigua de Perl, pero no más! Esto se puede ver que se ejecute B :: Deparse en su secuencia de comandos:

>perl -MO=Deparse
open my $fh, '<', $file or die "Could not open file $file for reading: $!\n";
while ( my $line = <$fh> ) {
  ...
}

^D
die "Could not open file $file for reading: $!\n" unless open my $fh, '<', $file;
while (defined(my $line = <$fh>)) {
    do {
        die 'Unimplemented'
    };
}
- syntax OK

Así que ya está bueno para ir!

Otros consejos

BTW, esto se cubre en la sección O Operadores I / de perldoc perlop :

En contexto escalar, la evaluación de un gestor de archivo en soportes de ángulo se obtiene la siguiente línea de ese archivo (el salto de línea, en su caso, incluido), o "undef" al final de su archivo o en error. Cuando $ / se establece en "no def" (a veces conocido como modo de archivo-slurp) y el archivo está vacío, devuelve '' la primera vez, seguido de "undef" posteriormente.

Por lo general usted debe asignar el valor devuelto a una variable, pero hay una situación en la que ocurre una asignación automática. Si y sólo si el símbolo de entrada es la única cosa en el interior del condicional de una sentencia "while" (aunque disfrazado de "for (;;)" bucle), el valor se asigna automáticamente a la variable global $ _, destruyendo todo lo fue allí anteriormente. (Esto puede parecer algo extraño, pero que va a utilizar la construcción en casi todos los scripts de Perl que escribe.) La variable $ _ no se localiza de manera implícita. Vas a tener que poner un "$ _ locales;" antes del bucle si quieres que eso ocurra.

Las siguientes líneas son equivalentes:

while (defined($_ = <STDIN>)) { print; }
while ($_ = <STDIN>) { print; }
while (<STDIN>) { print; }
for (;<STDIN>;) { print; }
print while defined($_ = <STDIN>);
print while ($_ = <STDIN>);
print while <STDIN>;

Esto también comporta de manera similar, pero evita _ $:

while (my $line = <STDIN>) { print $line }

En estas construcciones de bucle, se prueba a continuación, el valor asignado (si la asignación es automática o explícita) para ver si se ha definido. La prueba definido evita problemas en los que la línea tiene un valor de cadena que serían tratados como falsa por Perl, por ejemplo un "" o un "0", sin salto de línea final. Si realmente quieren decir para tales valores a terminar el bucle, deben hacerse la prueba de forma explícita:

while (($_ = <STDIN>) ne '0') { ... }
while (<STDIN>) { last unless $_; ... }

En otros contextos booleanos, "" sin una explícita "define" prueba o comparación provocan una advertencia si el pragma "uso advertencias" o el modificador de línea de comandos -w (el $ ^ W variable) está en vigor .

Si bien es cierto que la forma de while (my $line=<$fh>) { ... } obtiene compilado a while (defined( my $line = <$fh> ) ) { ... } considerar que hay una variedad de ocasiones en que una lectura legítima del valor '0' es malinterpretado si no tiene una defined explícita en el bucle o probar el regreso de <>.

Aquí hay varios ejemplos:

#!/usr/bin/perl
use strict; use warnings;

my $str = join "", map { "$_\n" } -10..10;
$str.="0";
my $sep='=' x 10;
my ($fh, $line);

open $fh, '<', \$str or 
     die "could not open in-memory file: $!";

print "$sep Should print:\n$str\n$sep\n";     

#Failure 1:
print 'while ($line=chomp_ln()) { print "$line\n"; }:',
      "\n";
while ($line=chomp_ln()) { print "$line\n"; } #fails on "0"
rewind();
print "$sep\n";

#Failure 2:
print 'while ($line=trim_ln()) { print "$line\n"; }',"\n";
while ($line=trim_ln()) { print "$line\n"; } #fails on "0"
print "$sep\n";
last_char();

#Failure 3:
# fails on last line of "0" 
print 'if(my $l=<$fh>) { print "$l\n" }', "\n";
if(my $l=<$fh>) { print "$l\n" } 
print "$sep\n";
last_char();

#Failure 4 and no Perl warning:
print 'print "$_\n" if <$fh>;',"\n";
print "$_\n" if <$fh>; #fails to print;
print "$sep\n";
last_char();

#Failure 5
# fails on last line of "0" with no Perl warning
print 'if($line=<$fh>) { print $line; }', "\n";
if($line=<$fh>) { 
    print $line; 
} else {
    print "READ ERROR: That was supposed to be the last line!\n";
}    
print "BUT, line read really was: \"$line\"", "\n\n";

sub chomp_ln {
# if I have "warnings", Perl says:
#    Value of <HANDLE> construct can be "0"; test with defined() 
    if($line=<$fh>) {
        chomp $line ;
        return $line;
    }
    return undef;
}

sub trim_ln {
# if I have "warnings", Perl says:
#    Value of <HANDLE> construct can be "0"; test with defined() 
    if (my $line=<$fh>) {
        $line =~ s/^\s+//;
        $line =~ s/\s+$//;
        return $line;
    }
    return undef;

}

sub rewind {
    seek ($fh, 0, 0) or 
        die "Cannot seek on in-memory file: $!";
}

sub last_char {
    seek($fh, -1, 2) or
       die "Cannot seek on in-memory file: $!";
}

No estoy diciendo que estas son buenas formas de Perl que estoy diciendo que son posibles!; El fracaso en especial 3,4 y 5. Nota del fracaso sin previo aviso Perl en el número 4 y 5. Los dos primeros tienen sus propios problemas ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top