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

时间:2015-11-05 02:06:12

标签: perl perl-module directory-structure data-files

将运行时必需数据文件与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安装的模块,而不是我正在尝试的与当前脚本相关的模块。

2 个答案:

答案 0 :(得分:5)

没有必要在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}; } 更容易分发。非编码人员可以更轻松地使用单独的并行文件。

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

{{1}}

现在每个对象都包含对共享字典的引用(不需要全局),只有在创建对象时才会加载。

答案 1 :(得分:0)

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

或者在编译库文件后立即使用UNITCHECK块可能更合适,这可以被认为是编译的扩展

package Dictionary;

use strict;
use warnings;

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