2010-01-07 2 views
4

필자는 숫자 열만으로 몇 개의 파일을 평균화하는 Perl 스크립트를 신속하게 작성했습니다. 그것은 파일 핸들들의 배열로부터 읽는 것을 포함한다. 다음은 스크립트입니다.배열 요소 인 Perl 파일 핸들에서 어떻게 읽을 수 있습니까?

#!/usr/local/bin/perl 

use strict; 
use warnings; 

use Symbol; 

die "Usage: $0 file1 [file2 ...]\n" unless scalar(@ARGV); 

my @fhs; 

foreach(@ARGV){ 
    my $fh = gensym; 
    open $fh, $_ or die "Unable to open \"$_\""; 
    push(@fhs, $fh); 
} 

while (scalar(@fhs)){ 
    my ($result, $n, $a, $i) = (0,0,0,0); 
    while ($i <= $#fhs){ 
     if ($a = <$fhs[$i]>){ 
      $result += $a; 
      $n++; 
      $i++; 
     } 
     else{ 
      $fhs[$i]->close; 
      splice(@fhs,$i,1); 
     } 
    } 
    if ($n){ print $result/$n . "\n"; } 
} 

이것은 작동하지 않습니다. 내가 스크립트를 디버깅 할 경우 내가 @fhs를 초기화 후에는 다음과 같습니다

DB<1> x @fhs 
0 GLOB(0x10443d80) 
    -> *Symbol::GEN0 
     FileHandle({*Symbol::GEN0}) => fileno(6) 
1 GLOB(0x10443e60) 
    -> *Symbol::GEN1 
     FileHandle({*Symbol::GEN1}) => fileno(7) 

를 지금까지 너무 좋아. 그러나 내가 파일에서 읽으려고하는 부분에서 실패

DB<3> x $fhs[$i] 
0 GLOB(0x10443d80) 
    -> *Symbol::GEN0 
     FileHandle({*Symbol::GEN0}) => fileno(6) 
    DB<4> x $a 
0 'GLOB(0x10443d80)' 

$ A는 글로브에서 읽은 문자열이 아닌 무언가로 가득 차 있습니다. 나는 무엇을 잘못 했는가?

+0

나는 당신에게 1000PP를 사용하여 멈추어 줄 것을 권할 것입니다. –

답변

12

<> 안에 간단한 스칼라 변수 만 사용하여 파일 핸들에서 읽을 수 있습니다. <$foo> 작품. <$foo[0]> 파일 핸들에서 읽지 않습니다. 실제로는 glob($foo[0])과 같습니다. 임시 변수 인 readline 내장 변수를 사용하거나 IO::File 및 OO 표기법을 사용해야합니다. 당신은 루프 내부의 배열에서 요소를 삭제되지 않은 경우

$text = readline($foo[0]); 
# or 
my $fh = $foo[0]; $text = <$fh>; 
# or 
$text = $foo[0]->getline; # If using IO::File 

, 당신은 쉽게 foreach 루프에 while 루프를 변경하여 임시 변수를 사용할 수 있습니다.

개인적으로는 파일 핸들을 만드는 데 gensym을 사용하는 것이 추악한 해킹이라고 생각합니다. IO :: File을 사용하거나 정의되지 않은 변수를 open으로 전달해야합니다 (Perl 5.6.0 이상이 필요하지만 현재는 10 년 전입니다). (그냥 대신 my $fh = gensym;my $fh; 말, 그리고 펄은 자동으로 새 파일 핸들을 만들고 open를 호출 할 때 $fh에 저장됩니다.)

+6

또는 'readline HANDLE'로 쓰여진' '와 동등합니다. –

1

내가 문제가 논리를 이해 있습니다. 숫자가 포함 된 여러 파일 (한 줄에 하나씩)을 읽고 그 평균을 인쇄하고 싶습니까? 당신이 마법의 비트를 사용하고자하는 경우

use strict; 
use warnings; 

my @fh; 
foreach my $f (@ARGV) { 
    open(my $fh, '<', $f) or die "Cannot open $f: $!"; 
    push @fh, $fh; 
} 

foreach my $fh (@fh) { 
    my ($sum, $n) = (0, 0); 
    while (<$fh>) { 
     $sum += $_; 
     $n++; 
    } 
    print "$sum/$n: ", $sum/$n, "\n" if $n; 
} 
+0

파일의 행 수가 같지 않다는 것이 문제입니다. –

+0

왜 그런가요? –

2

, 당신은 매우 간단하게이 작업을 수행 할 수는 gensym이 필요하지 않습니다에 대한 장난, 아주 오래된 펄을 사용하지 않는

use strict; 
use warnings; 

die "Usage: $0 file1 [file2 ...]\n" unless @ARGV; 

my $sum = 0; 

# The current filehandle is aliased to ARGV 
while (<>) { 
    $sum += $_; 
} 
continue { 
    # We have finished a file: 
    if(eof ARGV) { 
     # $. is the current line number. 
     print $sum/$. , "\n" if $.; 
     $sum = 0; 

     # Closing ARGV resets $. because ARGV is 
     # implicitly reopened for the next file. 
     close ARGV; 
    } 
} 

. IIRC, perl 5.6 및 이후 버전은 정상 어휘 처리에 만족합니다. open my $fh, '<', 'foo';

+1

나는 좋아한다. 그러나 '$ count'는 무엇입니까? – ephemient

+0

또한, 'unless'가 스칼라 컨텍스트에서 암시 적으로'@ ARGV'를 사용합니다 ...그리고 나는 이것을 마술로 간주하지 않을 것입니다, 이것 역시 제가 쓰는 방법입니다 :) – ephemient

+0

좋은 지적입니다. '스칼라가 아닌 한'은 OP의 코드를 붙여 넣기 만하면됩니다. 내가 라인 번호를 사용할 수 있다는 것을 깨닫기 전에 $ 카운트가 끝났습니다. – daotoad

1

for처럼 루프가 더 잘 작동하여 표준 읽기 (반복) 연산자를 실제로 사용할 수 있습니다.

for my $fh (@fhs) { 
    while (defined(my $line = <$fh>)) { 
     # since we're reading integers we test for *defined* 
     # so we don't close the file on '0' 
     #... 
    } 
    close $fh; 
} 

루프를 바로 가기처럼 보이지 않는 것처럼 보입니다. 따라서 while은 잘못된 루프 관용구 인 것 같습니다.

관련 문제