如何使用 Perl 模块包含数据文件?

How to include a data file with a Perl module?

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

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

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。我已经尝试了几种方法来让它工作,例如...

这里有类似的问题:How should I distribute data files with Perl modules?但是那个问题涉及 CPAN 安装的模块,而不是我正在尝试做的与当前脚本相关的模块。

无需在 BEGIN 时加载字典。 BEGIN 时间与正在加载的文件有关。当你的 main.pluse 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};
}

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

我建议使用 DATAINIT instead of BEGIN 以确保数据在 运行 时间之前初始化。它还使它更加自我记录

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

package Dictionary;

use strict;
use warnings;

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