【问题标题】:How to include a data file with a Perl module?如何在 Perl 模块中包含数据文件?
【发布时间】:2016-02-05 18:00:18
【问题描述】:

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

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

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") 的路径,而不是模块的路径,所以这给出了 File Not Found。

  • 使用数据:

    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 安装的模块,而不是我尝试做的与当前脚本相关的模块。

【问题讨论】:

  • 请注意,原型(即sub new())对方法没有影响。它们不是函数签名,they're something completely different。除非您知道自己在做什么,否则不要使用它们。如果您想要函数签名,请考虑 Method::SignaturesKavorkaFunction::Parameters
  • 我建议使用DATAINIT 而不是BEGIN 以确保在运行前初始化数据。它还使它更具自我记录性
  • @Schwern 糟糕,最近 C/C++ 太多了!我将编辑以删除这些。
  • 您还应该避免在方法标识符中使用大写字母。它们是为 Perl 全局变量保留的,例如包名,所以你的 sub Lookup 应该是 sub lookup
  • @Borodin 不保留大写方法名称。这不是普通的 Perl 风格,但它们不是保留的。

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


【解决方案1】:

无需在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};
}

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 2010-10-07
      • 2019-08-16
      • 2021-04-16
      • 1970-01-01
      相关资源
      最近更新 更多