内容

名称

DBM_Filter -- 过滤 DBM 键/值

概要

use DBM_Filter ;
use SDBM_File; # or DB_File, GDBM_File, NDBM_File, or ODBM_File

$db = tie %hash, ...

$db->Filter_Push(Fetch => sub {...},
                 Store => sub {...});

$db->Filter_Push('my_filter1');
$db->Filter_Push('my_filter2', params...);

$db->Filter_Key_Push(...) ;
$db->Filter_Value_Push(...) ;

$db->Filter_Pop();
$db->Filtered();

package DBM_Filter::my_filter1;

sub Store { ... }
sub Fetch { ... }

1;

package DBM_Filter::my_filter2;

sub Filter
{
    my @opts = @_;
    ...
    return (
        sub Store { ... },
        sub Fetch { ... } );
}

1;

说明

此模块提供了一个接口,允许将过滤器应用到与 DBM 文件关联的已绑定的哈希。它基于所有 *DB*_File 模块中存在的 DBM Filter 钩子,这些模块包含在从 5.6.1 版开始的标准 Perl 源代码发行版中。除了随 Perl 发行的 *DB*_File 模块之外,CPAN 上提供的 BerkeleyDB 模块也支持 DBM Filter 钩子。有关 DBM Filter 钩子的更多详细信息,请参阅 perldbmfilter

什么是 DBM Filter?

DBM Filter 允许在将键和/或值写入 DBM 文件之前以及在从 DBM 文件读回键和/或值之后,由用户定义的代码对其进行修改。例如,以下代码片段

$some_hash{"abc"} = 42;

可能会触发两个过滤器,一个用于写入键“abc”,另一个用于写入值 42。类似地,以下代码片段

my ($key, $value) = each %some_hash

将触发两个过滤器,一个用于读取键,另一个用于读取值。

与现有的 DBM Filter 功能类似,此模块安排使用键或值填充 $_ 变量,过滤器将检查该键或值。这通常意味着大多数 DBM 过滤器往往非常短。

有什么新功能?

与标准 DBM Filter 钩子相比,主要增强功能包括

方法

此模块将安排通过 tie 调用返回的对象使用以下方法。

$db->Filter_Push() / $db->Filter_Key_Push() / $db->Filter_Value_Push()

将过滤器添加到数据库 $db 的过滤器堆栈。这三种格式仅在它们应用于 DBM 键、DBM 值还是两者方面有所不同。

Filter_Push

该过滤器应用于键和值两者

Filter_Key_Push

过滤器仅应用于键

Filter_Value_Push

过滤器仅应用于值

$db->Filter_Pop()

如果存在,则移除应用于与$db关联的 DBM 文件的最后一个过滤器。

$db->Filtered()

如果对与$db关联的 DBM 应用了任何过滤器,则返回 TRUE。否则返回 FALSE。

编写过滤器

可以通过两种主要方式创建过滤器

立即过滤器

立即过滤器允许你在将过滤器应用于 dbm 的位置指定要使用的过滤器代码。在此模式中,Filter_*_Push 方法期望接收恰好两个参数。

my $db = tie %hash, 'SDBM_File', ...
$db->Filter_Push( Store => sub { },
                  Fetch => sub { });

Store关联的代码引用将在任何键/值写入数据库之前调用,而与Fetch关联的代码引用将在从数据库读取任何键/值之后调用。

例如,下面是一个示例过滤器,它在将所有字符串写入 DBM 文件之前向其添加一个尾随 NULL 字符,并在从 DBM 文件中读取这些字符串时移除尾随 NULL

my $db = tie %hash, 'SDBM_File', ...
$db->Filter_Push( Store => sub { $_ .= "\x00" ; },
                  Fetch => sub { s/\x00$// ;    });

需要注意的要点

  1. Store 和 Fetch 过滤器都操作$_

罐装过滤器

立即过滤器对于一次性情况很有用。对于更通用的问题,将过滤器打包到其自己的模块中可能很有用。

罐装过滤器的用法是

$db->Filter_Push("name", params)

其中

"name"

是要加载的模块的名称。如果指定字符串不包含包分隔符 "::",则假定它指的是完整模块名称 "DBM_Filter::name"。这意味着此模块中包含的罐装过滤器 "null" 和 "utf8" 的完整名称是

DBM_Filter::null
DBM_Filter::utf8
params

需要发送到过滤器的任何可选参数。请参阅 encode 过滤器,了解使用参数的模块示例。

实现罐装过滤器的模块可以采用两种形式之一。下面是第一个的模板

package DBM_Filter::null ;

use strict;
use warnings;

sub Store 
{
    # store code here    
}

sub Fetch
{
    # fetch code here
}

1;

注释

  1. 包名使用 DBM_Filter:: 前缀。

  2. 该模块必须同时具有 Store 和 Fetch 方法。如果只存在一个,或者都不存在,将抛出致命错误。

第二种形式允许过滤器使用闭包保存状态信息,因此

package DBM_Filter::encoding ;

use strict;
use warnings;

sub Filter
{
    my @params = @_ ;

    ...
    return {
        Store   => sub { $_ = $encoding->encode($_) },
        Fetch   => sub { $_ = $encoding->decode($_) }
        } ;
}

1;

在此实例中,“Store”和“Fetch”方法封装在“Filter”方法内。

包含的过滤器

此模块提供了一些罐头过滤器。它们涵盖了与 DBM 文件交互时需要过滤器的许多主要领域。它们还充当您自己的过滤器的模板。

包含的过滤器是

说明

维护往返完整性

在编写 DBM 过滤器时,确保在 DBM 过滤器就位时可以检索您已写入的所有数据非常重要。实际上,这意味着对 Store 方法中的数据应用任何转换,都应在 Fetch 方法中应用完全相反的操作。

如果您没有提供完全相反的转换,您会发现这样的代码不会按您预期的那样执行。

while (my ($k, $v) = each %hash)
{
    ...
}

根据转换,您会发现以下一个或多个情况会发生

  1. 循环永远不会终止。

  2. 检索的记录太少。

  3. 检索的记录太多。

  4. 循环会暂时做正确的事情,但它会意外失败。

不要在同一个数据库文件中混合过滤和非过滤数据。

这只是对前一节的重述。除非你完全确定自己知道自己在做什么,否则避免混合过滤和非过滤数据。

示例

假设你需要与一个旧版 C 应用程序进行交互,该应用程序将键存储为 C int,并将值和空终止 UTF-8 字符串存储。以下是设置方法

my $db = tie %hash, 'SDBM_File', ...

$db->Filter_Key_Push('int32') ;

$db->Filter_Value_Push('utf8');
$db->Filter_Value_Push('null');

另请参阅

<DB_File>、GDBM_FileNDBM_FileODBM_FileSDBM_Fileperldbmfilter

作者

Paul Marquess <[email protected]>