Domanda

Io di solito un ciclo tra le linee in un file utilizzando il seguente codice:

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

Tuttavia, nel rispondere a un'altra domanda , Evan Carroll modificato la mia risposta, cambiando la mia dichiarazione while a:

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

La sua logica era che, se si dispone di una linea che è 0 (che sarebbe dovuto essere l'ultima riga, altrimenti avrebbe un ritorno a capo) e poi il while sarebbe uscire prematuramente se si è utilizzato la mia affermazione ($line verrebbe impostato su "0", e il valore restituito dalla cessione sarebbero quindi anche "0" che viene valutata false). Se si controlla definito-ness, allora non si esegue in questo problema. Ha perfettamente senso.

Così ho provato. Ho creato un file di testo la cui linea ultimo è 0 senza ritorno del carrello su di esso. Ho eseguito attraverso il mio ciclo e il ciclo non abbia abbandonato prematuramente.

Poi ho pensato: "Ah, forse il valore non è in realtà 0, forse c'è qualcos'altro che di avvitare le cose!" Così ho usato Dump() da Devel::Peek e questo è ciò che mi ha dato:

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

che sembra dirmi che il valore è in realtà il "0" stringa, come ottengo un risultato simile se chiamo Dump() su un tipo scalare ho impostato in modo esplicito al "0" (l'unica differenza è nel campo LEN - dal file di LEN è 80, mentre dal scalare LEN è 8).

Allora, qual è il problema? Perché non il mio while() Exit Loop fa prematuramente se mi passa una linea che è solo "0" senza ritorno a capo? È anello di Evan in realtà più difensiva, o fa Perl fare qualcosa di pazzo internamente che significa che non c'è bisogno di preoccuparsi di queste cose e while() in realtà solo fa uscire quando si colpisce eof?

È stato utile?

Soluzione

A causa

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

in realtà compila fino a

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

Potrebbe essere stato necessaria in una versione molto vecchia di Perl, ma ora non più! Si può vedere questo esecuzione B :: Deparse sul tuo script:

>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

Quindi sei già pronti per partire!

Altri suggerimenti

A proposito, questo è coperto nella sezione O Operatori di I / perldoc perlop :

  

In un contesto scalare, valutare un filehandle tra parentesi angolari produce la riga successiva da quel file (il ritorno a capo, se del caso, incluso), o "undef" alla fine del file o in caso di errore. Quando $ / è impostato su "undef" (noto anche come modalità file-slurp) e il file è vuoto, restituisce '' la prima volta, seguito da "undef" successivamente.

     

In genere è necessario assegnare il valore restituito a una variabile, ma c'è una situazione in cui un assegnazione automatica accade. Se e solo se il simbolo di ingresso è l'unica cosa dentro il condizionale di un "po '" economico (anche se travestito da "for (;;)" loop), il valore viene automaticamente assegnato alla variabile globale $ _, distruggendo qualunque cosa c'era in precedenza. (Questo può sembrare una cosa strana a voi, ma che verrà utilizzato il costrutto in quasi tutti gli script in Perl che si scrive.) La variabile $ _ non è implicitamente localizzato. Dovrete mettere un "local $ _;" prima del ciclo, se si desidera che ciò accada.

     

Le righe seguenti sono equivalenti:

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

Questo comporta anche in modo simile, ma evita $ _:

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

In questi costrutti ciclo, il valore assegnato (se assegnamento è automatico o esplicito) viene testato per vedere se è definito. La prova definita evita problemi dove la linea ha un valore di stringa che sarebbero trattate come false di Perl, per esempio un "" o "0" senza fine riga. Se davvero intende per tali valori di interrompere il ciclo, essi dovrebbero essere testati per esplicitamente:

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

In altri contesti booleani, "" senza un esplicito "definito" test o confronto suscitano un avviso se il pragma "uso le avvertenze" oppure l'opzione della riga di comando -w (la variabile ^ W $) è a tutti gli effetti .

Se è vero che la forma di while (my $line=<$fh>) { ... } ottiene compilato per while (defined( my $line = <$fh> ) ) { ... } considerare ci sono una varietà di volte in cui una lettura legittima il valore '0' viene frainteso, se non si dispone di un defined esplicita nel ciclo o testare il ritorno di <>.

Ecco alcuni esempi:

#!/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: $!";
}

Non sono dicendo queste sono buone forme di Perl che sto dicendo che sono possibili!; soprattutto Fallimento 3,4 e 5. Nota del fallimento senza preavviso Perl sul numero 4 e 5. I primi due hanno i loro problemi ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top