将运行时必需数据文件与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'
...
);
工作,但显然是不可维护的。
类似的问题:How should I distribute data files with Perl modules?但是那个处理由CPAN安装的模块,而不是我正在尝试的与当前脚本相关的模块。
答案 0 :(得分:5)
没有必要在BEGIN
时加载字典。 BEGIN
时间相对于正在加载的文件。当您的main.pl
说use Dictionary
时,会编译并加载Dictionary.pm中的所有代码。把代码放在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};
}
更容易分发。非编码人员可以更轻松地使用单独的并行文件。
比加载库时加载整个字典更好,等待加载它时更有礼貌。
{{1}}
现在每个对象都包含对共享字典的引用(不需要全局),只有在创建对象时才会加载。
答案 1 :(得分:0)
我建议将DATA
与INIT
instead of BEGIN
一起使用,以确保在运行时之前初始化数据。它还使其更加自我记录
或者在编译库文件后立即使用UNITCHECK
块可能更合适,这可以被认为是编译的扩展
package Dictionary;
use strict;
use warnings;
my %dictionary;
UNITCHECK {
while ( <DATA> ) {
chomp;
my ($k, $v) = split /,/;
$dictionary{$k} = $v;
}
}