繁体   English   中英

如何在Perl模块中包含数据文件?

[英]How to include a data file with a Perl module?

将必需的运行时数据文件与Perl模块捆绑在一起的“正确”方法是什么,以便该模块在使用前可以读取其内容?

一个简单的例子就是这个Dictionary模块,它在启动时需要读取(单词,定义)对的列表。

package Reference::Dictionary;

# TODO: This is the Dictionary, which needs to be populated from
#  data-file BEFORE calling Lookup!
our %Dictionary;

sub new {
  my $class = shift;
  return bless {}, $class;
}

sub Lookup {
  my ($self,$word) = @_;
  return $Dictionary{$word};
}
1;

和一个驱动程序Main.pl:

use Reference::Dictionary;

my $dictionary = new Reference::Dictionary;
print $dictionary->Lookup("aardvark");

现在,我的目录结构如下所示:

root/
  Main.pl
  Reference/
    Dictionary.pm
    Dictionary.txt

我似乎无法在启动时获取Dictionary.pm来加载Dictionary.txt。 我尝试了几种方法来使它起作用,例如...

  • 使用BEGIN块:

     BEGIN { open(FP, '<', 'Dictionary.txt') or die "Can't open: $!\\n"; while (<FP>) { chomp; my ($word, $def) = split(/,/); $Dictionary{$word} = $def; } close(FP); } 

    没有骰子:Perl在cwd中寻找Dictionary.txt,这是主脚本(“ Main.pl”)的路径,而不是模块的路径,因此这将导致“找不到文件”。

  • 使用DATA:

     BEGIN { while (<DATA>) { chomp; my ($word, $def) = split(/,/); $Dictionary{$word} = $def; } close(DATA); } 

    在模块末尾

     __DATA__ aardvark,an animal which is definitely not an anteater abacus,an oldschool calculator ... 

    这也失败了,因为BEGIN在DATA可用之前在编译时执行。

  • 在模块中硬编码数据

     our %Dictionary = ( aardvark => 'an animal which is definitely not an anteater', abacus => 'an oldschool calculator' ... ); 

    可行,但绝对无法维护。

这里有类似的问题: 如何使用Perl模块分发数据文件? 但是那只涉及CPAN安装的模块,而不是我尝试做的相对于当前脚本的模块。

有没有需要加载字典, BEGIN时间。 BEGIN时间相对于要加载的文件。 main.pl说出use Dictionaryuse Dictionary中的所有代码都会被编译和加载。 将代码加载到Dictionary.pm中。

package Dictionary;

use strict;
use warnings;

my %Dictionary;  # There is no need for a global
while (<DATA>) {
    chomp;
    my ($word, $def) = split(/,/);
    $Dictionary{$word} = $def;
}

您也可以从同一目录中的Dictionary.txt加载。 诀窍是您必须提供文件的绝对路径。 您可以从__FILE__获得此文件,这是当前文件的路径(即Dictionary.pm )。

use File::Basename;

# Get the directory Dictionary.pm is located in.
my $dir = dirname(__FILE__);

open(my $fh, '<', "$dir/Dictionary.txt") or die "Can't open: $!\n";

my %Dictionary;
while (<$fh>) {
    chomp;
    my ($word, $def) = split(/,/);
    $Dictionary{$word} = $def;
}
close($fh);

您应该使用哪个? DATA更易于分发。 非编码人员更容易使用一个单独的并行文件。


比在加载库时加载整个字典更好,在需要时等待加载它更有礼貌。

use File::Basename;

# Load the dictionary from Dictionary.txt
sub _load_dictionary {
    my %dictionary;

    # Get the directory Dictionary.pm is located in.
    my $dir = dirname(__FILE__);

    open(my $fh, '<', "$dir/Dictionary.txt") or die "Can't open: $!\n";

    while (<$fh>) {
        chomp;
        my ($word, $def) = split(/,/);
        $dictionary{$word} = $def;
    }

    return \%dictionary;
}

# Get the possibly cached dictionary
my $Dictionary;
sub _get_dictionary {
    return $Dictionary ||= _load_dictionary;
}

sub new {
    my $class = shift;

    my $self = bless {}, $class;
    $self->{dictionary} = $self->_get_dictionary;

    return $self;
}

sub lookup {
    my $self = shift;
    my $word = shift;

    return $self->{dictionary}{$word};
}

现在,每个对象都包含对共享字典的引用(消除了对全局字典的需要),该引用仅在创建对象时加载。

我建议使用INIT而不是BEGIN DATA来确保数据在运行时被初始化。 它还使它更具自我记录性

或者使用UNITCHECK块可能更合适,该块将在编译库文件后立即尽早执行,因此可以视为编译的扩展

package Dictionary;

use strict;
use warnings;

my %dictionary;
UNITCHECK {
    while ( <DATA> ) {
        chomp;
        my ($k, $v) = split /,/;
        $dictionary{$k} = $v;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM