Доступ к оператору пустого угла Perl ‹› из реального дескриптора файла?

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

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


person Ryan C. Thompson    schedule 29.10.2009    source источник
comment
Непонятно, какое поведение ‹› вы ищете. Насколько мне известно, единственная разница между ‹› и ‹$filehandle› заключается в том, что ‹› будет использовать аргументы имени файла (@ARGV), если они заданы. Это то, что вы имели ввиду?   -  person dlowe    schedule 29.10.2009
comment
Я думаю, он хочет передать дескриптор файла <> функциям.   -  person Chris Lutz    schedule 29.10.2009
comment
Ааааа, теперь вижу. Я прочитал этот вопрос задом наперед...   -  person dlowe    schedule 29.10.2009
comment
Да, Крис, это именно то, что я хотел сделать. Но в итоге я использовал local @ARGV, а затем просто читал из <ARGV>. Спасибо всем.   -  person Ryan C. Thompson    schedule 29.10.2009


Ответы (3)


Из perldoc perlvar:

  • ARGV

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

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

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

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

person Chris Lutz    schedule 29.10.2009
comment
Конечно, я забыл другой ответ, который состоит в том, чтобы создать класс, который принимает много имен файлов и перегружает оператор <>, чтобы читать их так же, как <> делает с @ARGV. У него даже есть некоторый супер потенциал расширяемости. - person Chris Lutz; 29.10.2009
comment
Что хорошо в <>, так это то, что если нет имен файлов, он читается из STDIN. - person Sinan Ünür; 29.10.2009
comment
Да, потенциально мы бы потеряли это (было бы неудобно добавлять, по крайней мере), но мы получили бы возможность передавать это другим. - person Chris Lutz; 29.10.2009
comment
На самом деле, позже я узнал, что <> — это просто ярлык для <ARGV>. Я всегда думал, что это магия. Теперь я знаю, что настоящая магия кроется в дескрипторе файла ARGV. Но в любом случае, в конце концов я понял, что есть лучший способ структурировать мою программу. Однако я использовал трюк local @ARGV. - person Ryan C. Thompson; 29.10.2009

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

#!/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]:
person Sinan Ünür    schedule 29.10.2009
comment
Аааааа... [задумчиво смотрит на начало собственной реализации...] - person Chris Lutz; 29.10.2009
comment
Мне очень нравится ваша перегрузка интерпретации строк для печати текущего имени файла. Это действительно умно. - person Chris Lutz; 29.10.2009
comment
@ Крис Лутц: Спасибо. Наконец-то мне удалось выяснить, как получить доступ к текущему номеру строки через перегрузку. Веселые вещи. - person Sinan Ünür; 29.10.2009