[英]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 Dictionary
, use 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.