Accès à l'opérateur “< >” d'angle vide de Perl à partir d'un descripteur de fichier réel?

StackOverflow https://stackoverflow.com/questions/1640986

Question

J'aime utiliser la fonctionnalité astucieuse de Perl où la lecture de l'opérateur d'angle vide < > donne comme par magie à votre programme la sémantique de filtre UNIX, mais j'aimerais pouvoir accéder à cette fonctionnalité via un descripteur de fichier réel (ou un objet IO :: Handle , ou similaire) , afin que je puisse faire des choses comme le passer dans des sous-programmes et autres. Y a-t-il un moyen de le faire?

Cette question est particulièrement difficile à rechercher sur Google, car la recherche de "opérateur d'angle". et " filehandle " me dit simplement comment lire à partir de poignées de fichiers en utilisant l'opérateur d'angle.

Était-ce utile?

La solution

De perldoc perlvar :

  
      
  • ARGV
  •   
     

Le descripteur de fichier spécial qui parcourt les noms de fichiers de ligne de commande dans @ARGV . Généralement écrit en tant que descripteur de fichier null dans l'opérateur d'angle < > . Notez qu'actuellement ARGV n'a d'effet magique que dans l'opérateur < > ; ailleurs, il s’agit simplement d’un descripteur de fichier qui correspond au dernier fichier ouvert par < > . En particulier, le fait de transmettre \ * ARGV en tant que paramètre à une fonction qui attend un descripteur de fichier peut empêcher votre fonction de lire automatiquement le contenu de tous les fichiers contenus dans @ARGV . / p>

Je pense que cela répond à tous les aspects de votre question en indiquant que "je n'aime pas le dire, mais cela ne fera pas ce que vous voulez". genre de façon. Ce que vous pouvez faire est de créer des fonctions qui prennent une liste de noms de fichiers à ouvrir:

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

Mais c'est probablement le meilleur que vous puissiez gérer.

Autres conseils

En développant l’idée de Chris Lutz, voici une implémentation très rudimentaire:

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

pouvant être utilisé comme:

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, 

En développant l’idée de Chris Lutz, voici une implémentation très rudimentaire:

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

pouvant être utilisé comme:

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

Sortie:

<*> while <$it>; }

Sortie:

<*>

Il semble que cela ait déjà été mis en œuvre sous la forme Iterator :: Diamond . Iterator :: Diamond désactive également la magie ouverte à 2 arguments utilisée par perl lors de la lecture de < ARGV > . Mieux encore, il prend en charge la lecture de '-' en tant que STDIN, sans activer toute la magie. En fait, je pourrais l’utiliser à cette fin uniquement sur des fichiers uniques.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top