2010-09-22 7 views
14

मैं आमतौर पर निम्नलिखित कोड का उपयोग कर एक फ़ाइल की पंक्तियों के माध्यम से लूप:पर्ल के साथ फाइल में लाइनों के माध्यम से लूप करने का सबसे रक्षात्मक तरीका क्या है?

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

हालांकि, in answering another question, Evan Carroll मेरा उत्तर संपादित, करने के लिए अपने while बयान बदल रहा है:

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

उनका तर्क था कि यदि आप एक लाइन है जो 0 है (इसे अंतिम पंक्ति होना चाहिए, अन्यथा इसमें कैरिज रिटर्न होगा) तो आपके while यदि आप मेरे कथन का उपयोग करते हैं तो समय से बाहर निकल जाएगा ($lineपर सेट किया जाएगा, और असाइनमेंट से वापसी मूल्य भी "0" होगा जो गलत पर मूल्यांकन किया जाता है)। यदि आप परिभाषित-नस्ल की जांच करते हैं, तो आप इस समस्या में भाग नहीं लेते हैं। यह सही समझ में आता है।

तो मैंने कोशिश की। मैंने एक टेक्स्टफाइल बनाया जिसकी आखिरी पंक्ति 0 है जिसमें कोई कैरिज रिटर्न नहीं है। मैंने इसे अपने लूप के माध्यम से चलाया और लूप समय से बाहर नहीं निकल गया।

मैंने सोचा, "आह, शायद मूल्य वास्तव में 0 नहीं है, हो सकता है कि वहां कुछ और चीजें हैं जो चीजों को खराब कर रही हैं!" इसलिए मैं Devel::Peek से Dump() का इस्तेमाल किया और यह है कि क्या यह मुझे दिया है:

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

, मुझे बताने की कि मूल्य वास्तव में स्ट्रिंग "0" है के रूप में मैं एक ऐसी ही परिणाम प्राप्त अगर मैं एक अदिश मैं पर Dump() फोन लगता है कि स्पष्ट रूप से "0" पर सेट किया गया है (केवल अंतर LEN फ़ील्ड में है - फ़ाइल से LEN 80 है, जबकि स्केलर LEN से 8 है)।

तो सौदा क्या है? मेरे while() लूप से बाहर निकलने का समय क्यों नहीं है यदि मैं इसे एक लाइन पास करता हूं जो केवल "0" कैरिज रिटर्न के साथ है? क्या इवान का लूप वास्तव में अधिक रक्षात्मक है, या पर्ल आंतरिक रूप से कुछ पागल करता है जिसका मतलब है कि आपको इन चीजों के बारे में चिंता करने की आवश्यकता नहीं है और while() वास्तव में केवल eof पर क्लिक करते समय बाहर निकलता है?

+1

यदि आप रक्षात्मक कोड लिखना चाहते हैं, तो [टैंक] का उपयोग करें (http://en.wikipedia.org/wiki/Tank)। –

+3

यही कारण है कि मैं किसी की प्रतिक्रिया का अर्थ संपादित नहीं करूंगा (मैं केवल स्पष्ट टाइपो को ठीक करता हूं)। अगर आपको लगता है कि कुछ गुम है या इसमें सुधार किया जा सकता है तो इसके बजाय एक टिप्पणी जोड़ें। और आंतरिक जांच के लिए आप के लिए kudos! – Ether

उत्तर

18

क्योंकि

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

वास्तव में

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

करने के लिए नीचे संकलित यह पर्ल का एक बहुत ही पुराने संस्करण में आवश्यक हो सकता है किया गया है, लेकिन नहीं किसी भी अधिक! आप इसे अपनी स्क्रिप्ट पर बी :: डिस्प्रेस चलाने से देख सकते हैं:

>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 

तो आप पहले से ही जाने के लिए अच्छे हैं!

+1

पीएस, मुझे प्यार है ... बिल्कुल प्यार है कि कैसे ... ~ '5.12 और ऊपर वैध वाक्यविन्यास है। इसे प्यार करना। –

+0

ओह माय। मुझे आश्चर्य है कि अगर किसी को भी उस अंतर्निहित 'परिभाषित' द्वारा गधे में काटा जाता है? – zigdon

+0

यदि कोई वास्तव में कोड लिख रहा है जो जांच कर रहा है कि एक अन-चॉम्प() एड लाइन किसी फ़ाइल से बिना किसी लाइन के अंत में गलत तरीके से मूल्यांकन करती है, तो वे वास्तव में वही प्राप्त कर रहे हैं जो वे लायक हैं। पर्ल के डीडब्ल्यूआईएम रवैये आमतौर पर चीजें सही हो जाता है। – geoffspear

13

BTW, यह मैं/perldoc perlop हे ऑपरेटर्स खंड में कवर किया जाता है:

अदिश संदर्भ में, कोण कोष्ठक में एक filehandle का मूल्यांकन कि फ़ाइल से अगली पंक्ति (newline, यदि कोई हो, शामिल पैदावार), या फ़ाइल के अंत में या त्रुटि पर "undef"। जब $/को "undef" (कभी-कभी फ़ाइल-स्लर्प मोड के रूप में जाना जाता है) पर सेट किया जाता है और फ़ाइल खाली होती है, तो यह पहली बार "अंडेफ" के बाद "पहली बार" लौटाती है।

आमतौर पर आपको लौटे हुए मान को एक चर में असाइन करना होगा, लेकिन एक ऐसी स्थिति है जहां एक स्वचालित असाइनमेंट होता है।अगर और केवल अगर इनपुट प्रतीक "थोड़ी देर" कथन की सशर्त के अंदर एकमात्र चीज है (भले ही "के लिए (;;)" लूप के रूप में छिपी हुई हो), मान स्वचालित रूप से वैश्विक चर $ _ को सौंपा गया है, जो कुछ भी नष्ट कर रहा है पहले वहाँ था (यह आपके लिए एक अजीब चीज की तरह प्रतीत हो सकता है, लेकिन आप लिखने वाली लगभग हर पर्ल स्क्रिप्ट में निर्माण का उपयोग करेंगे।) $ _ चर को स्पष्ट रूप से स्थानीयकृत नहीं किया गया है। आपको "स्थानीय $ _;" रखना होगा यदि आप ऐसा करना चाहते हैं तो लूप से पहले।

निम्नलिखित लाइनों के बराबर हैं:

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

यह भी इसी तरह व्यवहार करती है, लेकिन $ बचा जाता है _:

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

इन पाश निर्माणों में, असाइन मूल्य (चाहे काम स्वत: या स्पष्ट है) तब यह जांचने के लिए परीक्षण किया जाता है कि यह परिभाषित किया गया है या नहीं। परिभाषित परीक्षण उन समस्याओं से बचाता है जहां रेखा में एक स्ट्रिंग मान होता है जिसे पर्ल द्वारा गलत माना जाएगा, उदाहरण के लिए "" या "0" कोई पिछली नईलाइन नहीं है। क्या तुम सच में इस तरह के मूल्यों के लिए इसका मतलब यह तो पाश समाप्त करने के लिए, वे स्पष्ट रूप से परीक्षण किया जाना चाहिए:

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

अन्य बूलियन संदर्भों में, "<filehandle>" बिना एक स्पष्ट "परिभाषित" परीक्षण या तुलना में एक चेतावनी है, तो प्रकाश में लाना "चेतावनियों का उपयोग करें" प्रज्ञा या -w कमांड लाइन स्विच ($^डब्ल्यू चर) प्रभाव में है।

+1

अच्छा जवाब इसलिए मैंने अपना हटा दिया। लेकिन पर्ल डॉक्स भ्रामक हैं - वे कहते हैं, "यदि ** ** और केवल अगर ** इनपुट प्रतीक कुछ समय के सशर्त के अंदर एकमात्र चीज है" - लेकिन बाद में "और केवल अगर" यह दिखाकर भाग 'जबकि (मेरी $ line = ) 'वैसे ही व्यवहार करता है। हमें इस बारे में सोचते हुए कि इस डीडब्ल्यूआईएमएमरी में कौन सी परिस्थितियों में प्रदर्शन किया जाएगा। –

+1

@j_random: "अगर और केवल अगर" भाग यह संदर्भित करता है कि $ _ को हैंडल से पढ़ने वाली रेखा के स्थान के रूप में उपयोग किया जाता है, न कि क्या 'परिभाषित' तर्क नियोजित है? – Ether

+0

आप बिल्कुल सही हैं, मेरे हिस्से पर खराब पढ़ने की समझ है। मैं क्षमाप्रार्थी हूं। मुझे अभी भी लगता है कि यह ठीक होने के बारे में स्पष्ट नहीं होगा जब 'परिभाषित' स्वतः लागू होता है। मेरा अनुमान है: यदि लूप सशर्त परीक्षण '' या आरएचएस पर '' के साथ एक स्केलर असाइनमेंट है - क्या यह सबकुछ है? –

1

हालांकि यह सही है कि while (my $line=<$fh>) { ... } के रूप compiledwhile (defined(my $line = <$fh>)) { ... } पर विचार बार की एक किस्म देखते हैं अगर आप एक स्पष्ट defined पाश में नहीं है या जब मूल्य "0" के एक वैध पढ़ने की गलत व्याख्या की है हो जाता है <> की वापसी का परीक्षण।

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

मैं नहीं कह रहा हूँ इन पर्ल की अच्छी प्रकार हैं:

यहाँ कई उदाहरण हैं! मैं कह रहा हूं कि वे संभव हैं; विशेष रूप से विफलता 3,4 और 5. संख्या 4 और 5 पर कोई पर्ल चेतावनी के साथ विफलता पर ध्यान दें। पहले दो में अपने स्वयं के मुद्दे हैं ...

संबंधित मुद्दे

 संबंधित मुद्दे