麻烦将IO :: File句柄传递给Perl类

时间:2017-04-04 18:21:02

标签: perl

如果我将其作为参数传递,我会收到错误:

'找不到对象方法" getline"通过包"坏"在Bad.pm第27行。'

但是如果我将它插入模块就可以了。 这是简化的代码。 bad.pl使用Bad.pm模块。设置$ CAUSE_ERROR以查看问题。

#!/usr/bin/env perl
# This is bad.pl

use strict;
use warnings;
use IO::File;
use Bad;    # use the bad module "Bad.pm"

&Main();

sub Main {
    my $filename = "bad.pl";
    warn "About to parse '$filename'\n";
    my $MyWord = Bad->new();   # Create a new object.

    my $io = IO::File->new($filename, "r");

    #####################
    my $CAUSE_ERROR  = 1;   # Set to 0 it does NOT cause an error. Set to 1 it DOES.
    #####################

    if($CAUSE_ERROR ) {
        $MyWord->Parse($MyWord, $io);
    } else {
        $MyWord->{fd} = $io;
        $MyWord->Parse($MyWord);
    }
}

这是Bad.pm

package Bad;
# This is Bad.pm

use warnings;
use strict;

sub new {
    my ($class, $args) = @_;
    my $self = {
        fd => undef,
    };
    return bless($self, $class);       # Changes a function to a class
}

sub Parse {
    my ($MyWord, $io) = @_;

    if(defined($MyWord->{fd})){
        # WORKS
        $io = $MyWord->{fd};
        while ( defined(my $inputline = $io->getline) ) {
            print "${inputline}";
        }
    } else {
        # FAILS
        while ( defined(my $inputline = $io->getline) ) {
            print "${inputline}";
        }
    }
}
1;

在Cygwin下使用Perl v5.22.3。 最初我在子目录中有Bad.pm,但我简化了它。 谢谢你的时间。

2 个答案:

答案 0 :(得分:0)

总结发布代码中的错误:类名被传递给幕后的构造函数,方法的对象也是如此;我们供应它们。我们确实将文件句柄传递给new,以便将其分配给对象的数据,因此可以由类中的方法使用。

这是一个基本的例子。我尽可能地坚持发布的设计。这对I / O对象所需要的内容并不多,而是通常编写类。

该类用于处理文件,已为其传递文件句柄。我们希望每个对象有一个文件句柄。由于我们打开它,关闭它的责任留给了来电者。

<强> script.pl

use strict;
use warnings;
use feature 'say';    
use IO::File;

use ProcessFile;

my $filename = shift || $0;         # from command line, or this file    
say "About to parse '$filename'";    
my $io = IO::File->new($filename, "r") or die "Can't open $filename: $!";

my $word = ProcessFile->new($io);   # Create a new object, initialize with $io
$word->parse();
# OR, by chaining calls
#my $word = ProcessFile->new($io)->parse();

say "Have ", ProcessFile->num_objects(), " open filehandles";    

$io->close;

包文件 ProcessFile.pm

package ProcessFile;

use warnings;
use strict;
use Carp qw(croak);
use Scalar::Util qw(openhandle);

# Example of "Class" data and methods: how many objects (open filehandles)
our $NumObjects;
sub num_objects  { return $NumObjects }
sub DESTROY      { --$NumObjects }

sub new {
    my ($class, $fh) = @_;   # class name, arguments passed to constructor    
    # To also check the mode (must be opened for reading) use Fcntl module
    croak "No filehandle or not open or invalid " if not openhandle $fh;

    my $self = { _fh => $fh };  # add other data that may make sense
    bless $self, $class;        # now $self is an object of class ProcessFile

    ++$NumObjects;
    return $self;
}

sub parse {
    my ($self, @args) = @_;  # object, arguments passed to method (if any)

    # Filehandle is retrieved from data, $self->{_fh}
    while ( defined(my $inputline = $self->{_fh}->getline) ) {
        print $inputline;
    }

    # Rewind before returning $self (or not, depending on design/@args)
    # Can do more here, set some data etc, as needed by class design
    seek $self->{_fh}, 0, 0;
    return $self;
}

1;

关于上述代码的一些评论如下。让我知道是否会有更多帮助。

数据和方法不属于任何一个对象,并且用于与整个类相关的目的(例如,跟踪播放中的所有对象)。

DESTROY方法在对象被销毁时运行,例如当它超出范围时。在这里我们需要它以减少现有对象的数量。尝试:将代码创建一个对象放在一个块{ ... };中,看看我们在块后得到的数量。

我们使用Scalar::Util中的openhandle来测试文件句柄是否已打开。我们还应该测试它是否对阅读是开放的,因为这是该类的固定目的,使用Fcntl

在唯一的示例方法parse中,我们读出文件,然后在返回对象之前回退文件句柄。这是一个占位符,用于保存和/或设置重复使用的状态。所做的工作取决于课程的目的和设计,并可以通过参数进行控制。

文档:教程perlootut和参考perlobj关于Perl中面向对象的工作,perlmod关于模块(类首先是包)和教程perlreftut供参考。

周围也有很多信息性的SO帖子,请搜索。

答案 1 :(得分:0)

总结:

$MyWord->Parse($MyWord, $io);

鉴于$MyWordBad类中的一个引用(即,它是Bad的实例),这将使用参数{{1}调用Bad::Parse }。也就是说,它的行为就像你打电话:

($MyWord, $MyWord, $io)

但是,Bad::Parse($MyWord, $MyWord, $io)`. 编写为期望参数Bad::Parse(),因此($MyWord, $io)设置为第二个$io,而$MyWord会引发错误尝试调用Bad::Parse(),因为$io->getline模块没有实现该方法。

修复很简单:

  1. 将该功能称为Bad

  2. $MyWord->Parse($io)中第一个参数的变量名称从Bad::Parse()更改为$MyWord。这不是严格的必要 - 你可以在技术上调用这个变量 - 但它是常规的,并且会使你的代码对其他Perl程序员更具可读性。