2009-10-29 7 views
4

Мне нравится использовать отличную функцию perl, где чтение из оператора пустого угла <> волшебным образом дает вашу семантику фильтра UNIX-программы, но я хотел бы иметь доступ к этой функции через фактический дескриптор файла (или объект IO::Handle или аналогичный) , так что я могу делать что-то вроде передачи его в подпрограммы и т. д. Есть какой-либо способ сделать это?Доступ к свободному угловому указателю Perl «<>» из фактического дескриптора файла?

Этот вопрос особенно тяжел для Google, потому что поиск «оператора угла» и «дескриптор файла» просто подсказывает, как читать из дескрипторов файлов с помощью оператора угла.

+0

Непонятно, какое поведение <> вы ищете. Насколько мне известно, единственная разница между <> и <$filehandle> заключается в том, что <> вернется к аргументам имени файла (@ARGV), если они указаны. Это то, что вы имели ввиду? – dlowe

+0

Я думаю, он хочет передать '<>' дескриптор файла для функций. –

+0

Aahhhh, я вижу сейчас. Я прочитал этот вопрос назад ... – dlowe

ответ

9

От perldoc perlvar:

  • ARGV

Специальная указатель_на_файл, перебирающий командной строки имен файлов в @ARGV. Обычно записывается как нулевой дескриптор файла в операторе угла <>. Обратите внимание, что в настоящий момент ARGV имеет магический эффект только в операторе <>; в другом месте это просто простой дескриптор файла, соответствующий последнему файлу, открытому <>. В частности, передача \*ARGV в качестве параметра функции, которая ожидает, что дескриптор файла не может заставить вашу функцию автоматически считывать содержимое всех файлов в @ARGV.

Я считаю, что ответы на все аспекты вашего вопроса в том, что «Ненависть, чтобы сказать это, но он не будет делать то, что вы хотите». Что вы могли бы сделать, это функции, которые принимают список имен файлов, чтобы открыть, и сделать это:

sub takes_filenames (@) { 
    local @ARGV = @_; 
    // do stuff with <> 
} 

Но это, пожалуй, лучшее, что вы будете в состоянии управлять.

+0

Конечно, я забыл другой ответ, который должен сделать класс, который принимает много имен файлов и перегружает оператор '<>', чтобы читать их так же, как '<>' с помощью '@ ARGV'. Он даже обладает некоторым потенциалом с возможностью расширения. –

+1

Хорошая вещь о '<>' - если нет имен файлов, она читается из 'STDIN'. –

+0

Да, мы могли бы потерять это (было бы неудобно добавлять, по крайней мере), но мы получили бы возможность передать его. –

6

Развивая идеи Криса Лутц, вот это очень рудиментарным реализация:

#!/usr/bin/perl 

package My::ARGV::Reader; 

use strict; use warnings; 
use autodie; 
use IO::Handle; 

use overload 
    '<>' => \&reader, 
    '""' => \&argv, 
    '0+' => \&input_line_number, 
; 

sub new { 
    my $class = shift; 
    my $self = { 
     names => [ @_ ], 
     handles => [], 
     current_file => 0, 
    }; 
    bless $self => $class; 
} 

sub reader { 
    my $self = shift; 

    return scalar <STDIN> unless @{ $self->{names}}; 

    my $line; 

    while (1) { 
     my $current = $self->{current_file}; 
     return if $current >= @{ $self->{names} }; 

     my $fh = $self->{handles}->[$current]; 

     unless ($fh) { 
      $self->{handles}->[$current] = $fh = $self->open_file; 
     } 

     if(eof $fh) { 
      close $fh; 
      $self->{current_file} = $current + 1; 
      next; 
     } 

     $line = <$fh>; 
     last; 
    } 
    return $line; 
} 

sub open_file { 
    my $self = shift; 
    my $name = $self->{names}->[ $self->{current_file} ]; 
    open my $fh, '<', $name; 
    return $fh; 
} 

sub argv { 
    my $self = shift; 
    my $name = @{$self->{names}} 
      ? $self->{names}->[ $self->{current_file} ] 
      : '-' 
      ; 
    return $name; 
} 

sub input_line_number { 
    my $self = shift; 
    my $fh = @{$self->{names}} 
      ? $self->{handles}->[$self->{current_file}] 
      : \*STDIN 
      ; 
    return $fh->input_line_number; 
} 

, которые могут быть использованы в качестве:

package main; 

use strict; use warnings; 

my $it = My::ARGV::Reader->new(@ARGV); 

echo($it); 

sub echo { 
    my ($it) = @_; 
    printf "[%s:%d]:%s", $it, +$it, $_ while <$it>; 
} 

Выход:

 
[file1:1]:bye bye 
[file1:2]:hello 
[file1:3]:thank you 
[file1:4]:no translation 
[file1:5]: 
[file2:1]:chao 
[file2:2]:hola 
[file2:3]:gracias 
[file2:4]: 
+3

Awww ... [смотрит виртуозно на начало собственной реализации ...] –

+2

Мне очень нравится перегрузка интерпретации строк для печати текущего имени файла. Это действительно умно. –

+1

@ Крис Лутц: Спасибо. Наконец, мне удалось выяснить, как получить доступ к текущему номеру строки через перегрузку. Забавные вещи. –

1

Похоже, что это уже реализован как Iterator::Diamond. Итератор :: Diamond также отключает открытую магию с двумя аргументами, используемую perl при чтении <ARGV>. Еще лучше, он поддерживает чтение '-' как STDIN, не позволяя всем другим магии. На самом деле, я могу использовать его для этой цели только для отдельных файлов.