2015-11-05 65 views
1

将运行时需要的数据文件与Perl模块捆绑在一起的“正确”方式是什么,以便模块在使用前可以读取其内容?如何使用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”)的路径,而不是模块的路径,所以这会给出File Not Found。

  • 使用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

请注意,原型(即'sub new()')对方法有*无效。它们不是函数签名,[它们是完全不同的](https://stackoverflow.com/questions/297034/why-are-perl-5s-function-prototypes-bad)。除非你知道你在做什么,否则不要使用它们。如果您想要函数签名,请考虑[Method :: Signatures](https://metacpan.org/pod/Method:Signatures),[Kavorka](https://metacpan.org/pod/Kavorka)或[Function ::参数](https://metacpan.org/pod/Function::Parameters)。 – Schwern

+0

我建议在'INIT'而不是'BEGIN'中使用'DATA'来确保数据在运行时间之前被初始化。它也使它更自我记录 – Borodin

+0

@Schwern最近哎呀我的坏,太多的C/C++!我将编辑删除这些。 –

回答

4

有没有必要加载字典在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}; 
} 

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

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; 
    } 
}