Pregunta

Me gusta usar la ingeniosa función perl donde leer desde el operador de ángulo vacío < > mágicamente le da a su programa semántica de filtro UNIX, pero me gustaría poder acceder a esta función a través de un identificador de archivo real (o IO :: Handle , o similar) , para poder hacer cosas como pasarlo a subrutinas y demás. ¿Hay alguna forma de hacer esto?

Esta pregunta es particularmente difícil de google, porque la búsqueda de " operador de ángulo " y "filehandle" solo me dice cómo leer de los controladores de archivos usando el operador de ángulo.

¿Fue útil?

Solución

De perldoc perlvar :

  
      
  • ARGV
  •   
     

El identificador de archivo especial que itera sobre los nombres de archivo de la línea de comandos en @ARGV . Por lo general, se escribe como el identificador de archivo nulo en el operador de ángulo < > . Tenga en cuenta que actualmente ARGV solo tiene su efecto mágico dentro del operador < > ; en otros lugares es solo un identificador de archivo simple correspondiente al último archivo abierto por < > . En particular, pasar \ * ARGV como parámetro a una función que espera un identificador de archivo puede no hacer que su función lea automáticamente el contenido de todos los archivos en @ARGV .

Creo que eso responde a todos los aspectos de su pregunta en ese " Odio decirlo, pero no hará lo que quiere " tipo de camino Lo que podría hacer es crear funciones que tomen una lista de nombres de archivo para abrir, y haga esto:

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

Pero eso es probablemente lo mejor que podrá administrar.

Otros consejos

Ampliando la idea de Chris Lutz, aquí hay una implementación muy rudimentaria:

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

que se puede usar como:

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, 

Ampliando la idea de Chris Lutz, aquí hay una implementación muy rudimentaria:

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

que se puede usar como:

[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]:

Salida:

<*> while <$it>; }

Salida:

<*>

Parece que esto ya se ha implementado como Iterator :: Diamond . Iterator :: Diamond también deshabilita la magia abierta de 2 argumentos que Perl usa cuando lee < ARGV > . Aún mejor, admite leer '-' como STDIN, sin habilitar toda la otra magia. De hecho, podría usarlo para ese propósito solo en archivos individuales.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top