Was ist die defensive Art und Weise in einer Schleife durch die Leitungen in einer Datei mit Perl?

StackOverflow https://stackoverflow.com/questions/3773917

Frage

ich in der Regel einer Schleife durch Zeilen in einer Datei mit dem folgenden Code:

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

Doch in einer anderen Frage beantworten, Evan Carroll editierten meine Antwort auf meine while Aussage zu ändern:

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

Seine Begründung war, dass, wenn Sie eine Linie haben, die 0 ist (es muss die letzte Zeile sein würde, sonst hätte es einen Wagenrücklauf hat) dann while würde vorzeitig beenden, wenn Sie meine Anweisung ($line eingestellt werden würde "0", und der Rückgabewert aus der Zuordnung wäre also auch "0", was zu falsch ausgewertet wird). Wenn Sie definiert-ness überprüfen, dann müssen Sie nicht in dieses Problem. Es macht durchaus Sinn.

versucht, damit ich es. Ich habe eine Textdatei, deren letzte Zeile ist 0 ohne Wagenrücklauf darauf. Ich lief es durch meine Schleife und die Schleife verlassen nicht vorzeitig.

Dann dachte ich: „Aha, vielleicht ist der Wert nicht tatsächlich 0, vielleicht ist es etwas anderes gibt, dass die Dinge Verschrauben!“ Also ich Dump() von Devel::Peek verwendet und das ist, was es gab mir:

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

Das scheint mir zu sagen, dass der Wert tatsächlich der String "0" ist, da ich ein ähnliches Ergebnis, wenn ich Dump() auf einem skalaren nennen habe ich explizit auf "0" gesetzt (der einzige Unterschied ist im Bereich LEN - von der LEN-Datei 80 ist, wohingegen aus den skalaren LEN ist 8).

Also, was ist das Problem? Warum meine while() Schleife nicht Ausfahrt vorzeitig, wenn ich pass es eine Linie, die ohne Wagenrücklauf nur "0" ist? Ist Evans Schleife tatsächlich defensiver oder tut Perl tun etwas intern verrückt, dass Mittel Sie nicht über diese Dinge und while() zu sorgen brauchen eigentlich nur, dass beendet werden, wenn Sie treffen eof?

War es hilfreich?

Lösung

Da

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

kompiliert tatsächlich bis zu

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

Es kann in einer sehr alten Version von Perl, aber nicht mehr nötig gewesen!

: Sie können B :: Deparse auf Ihrem Skript laufen sehen diese
>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

So können Sie bereits gut sind zu gehen!

Andere Tipps

BTW, dies in dem I / O-Operatoren Abschnitt behandelt wird von Perldoc perlop

Skalarkontext, einen Dateihandle in spitzen Klammern Auswertung ergibt die nächste Zeile aus der Datei (die Neuen-Zeile, sofern vorhanden, enthält) oder „undefiniert“ am Ende der Datei oder einem Fehler auftritt. Wenn $ / wird auf „undefiniert“ (manchmal auch als Datei-schlürfen Modus bekannt) und die Datei ist leer, es gibt ‚‘ das erste Mal, gefolgt von „undefiniert“ anschließend.

Normalerweise müssen Sie den Rückgabewert einer Variablen zuweisen, aber es ist eine Situation, in der eine automatische Zuordnung geschieht. Wenn und nur wenn das Eingangssymbol ist das einzige, was im Innern der bedingten ein „während“ Erklärung (auch wenn als ein verschleierten „für (;;)“ Loop), wird der Wert automatisch auf die globale Variable $ zugewiesen _, zu zerstören was auch immer zuvor war dort. (Dies mag wie eine seltsame Sache zu Ihnen scheinen, aber Sie werden das Konstrukt in fast jedem Perl-Skript verwenden Sie schreiben.) Die Variable $ _ ist nicht implizit lokalisiert. Sie werden eine setzen müssen „local $ _;“ wenn Sie vor der Schleife wollen, dass das geschehen.

Die folgenden Zeilen sind äquivalent:

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

Dies verhält sich auch ähnlich, aber vermeidet $ _:

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

In diesen Schleifenkonstrukten, der zugewiesene Wert (ob Zuordnung automatisch oder explizit ist) wird dann geprüft, um zu sehen, ob sie definiert ist. Der definierte Test vermeidet Probleme, wo Linie einen String-Wert hat, der zum Beispiel ein von Perl, als falsch behandelt würden „“ oder eine „0“ ohne Newline. Wenn Sie wirklich für solche Werte bedeuten, die Schleife zu beenden, sollten sie explizit getestet werden:

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

In anderen boolean Kontexten „“ ohne explizite „definiert“ Test oder Vergleich entlocken eine Warnung, wenn die „Verwendung Warnungen“ Pragma oder die -w Befehlszeilenoption (die $ ^ W variabel) in Kraft ist, .

Während es richtig ist, dass die Form des while (my $line=<$fh>) { ... } bekommt zusammengestellt while (defined( my $line = <$fh> ) ) { ... } betrachtet es eine Vielzahl von Zeiten, wenn ein berechtigtes Lesen des Wertes ‚0‘ falsch interpretiert wird, wenn Sie in der Schleife eine explizite defined nicht haben oder testen der Rückkehr von <>.

Hier sind einige Beispiele:

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

Ich sage nicht, diese sind gute Formen von Perl Ich sage, dass sie möglich sind!; insbesondere Failure 3,4 und 5. Beachten Sie die Störung ohne Warnung Perl auf Nummer 4 und 5. Die ersten beiden ihre eigenen Probleme haben ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top