Filter::Util::Call - Perl 源代码过滤器实用程序模块
use Filter::Util::Call ;
此模块为您提供了一个框架,以便在 Perl 中编写源代码过滤器。
现在可以使用 Filter::Util::Call 的备用接口。有关更多详细信息,请参阅 Filter::Simple。
Perl 源代码过滤器以 Perl 模块的形式实现。模块的结构可以采用两种大致类似的格式。为了区分它们,第一个将被称为方法过滤器,第二个将被称为闭包过滤器。
以下是方法过滤器的框架
package MyFilter ;
use Filter::Util::Call ;
sub import
{
my($type, @arguments) = @_ ;
filter_add([]) ;
}
sub filter
{
my($self) = @_ ;
my($status) ;
$status = filter_read() ;
$status ;
}
1 ;
这是闭包过滤器的等效框架
package MyFilter ;
use Filter::Util::Call ;
sub import
{
my($type, @arguments) = @_ ;
filter_add(
sub
{
my($status) ;
$status = filter_read() ;
$status ;
} )
}
1 ;
要使用上述两个过滤器模块中的任何一个,请在 Perl 源代码文件中放置以下行。
use MyFilter;
事实上,上面显示的框架模块是功能齐全的源代码过滤器,尽管它们相当无用。它们所做的只是过滤源流,而根本不修改它。
您会发现,这两个模块的结构非常相似。它们都使用 Filter::Util::Call
模块,并且都具有 import
方法。它们之间的区别在于,方法过滤器需要一个 过滤器方法,而闭包过滤器通过传递给 filter_add 的匿名子例程获取相当于 过滤器方法的内容。
要正确使用上面显示的闭包过滤器,您需要充分理解闭包的概念。有关闭包机制的更多详细信息,请参阅 perlref。
Filter::Util::Call
导出了以下函数
filter_add()
filter_read()
filter_read_exact()
filter_del()
import
方法用于创建过滤器的实例。当源文件中遇到 use MyFilter
行时,Perl 会间接调用此方法(有关 import
的更多详细信息,请参阅 perlfunc 中的“import”)。
它总是至少有一个由 Perl 自动传递的参数 - 这对应于包的名称。在上面的示例中,它将是 "MyFilter"
。
除了第一个参数外,import 还可以接受一个可选参数列表。这些参数可用于将参数传递给过滤器。例如
use MyFilter qw(a b c) ;
将导致 @_
数组具有以下值
@_ [0] => "MyFilter"
@_ [1] => "a"
@_ [2] => "b"
@_ [3] => "c"
在终止之前,import
函数必须通过调用 filter_add
来显式安装过滤器。
函数 filter_add
实际上会安装过滤器。它需要一个参数,该参数应为引用。所使用的引用的类型将决定使用哪两种过滤器类型中的一种。
如果使用 CODE 引用,则会假定为闭包过滤器。
如果不使用 CODE 引用,则会假定为方法过滤器。在方法过滤器中,引用可用于存储上下文信息。除非引用已被祝福,否则 filter_add
会将引用祝福到包中。
有关使用方法过滤器和闭包过滤器使用上下文信息的示例,请参阅本文档末尾的过滤器。
使用方法过滤器的 filter
方法和使用闭包过滤器的匿名子程序都是完成过滤器主要处理的地方。
这两种过滤器类型之间的主要区别在于,方法过滤器使用传递给该方法的对象来存储任何上下文数据,而闭包过滤器使用闭包维护的词法变量。
请注意,传递给方法过滤器的单个参数 $self
与传递给 filter_add
并已纳入过滤器的包中的引用相同。有关使用 $self
的详细信息,请参阅后面的示例过滤器。
以下是匿名子程序和 filter()
方法的常见功能列表。
虽然 $_
实际上并未明确出现在上面的示例过滤器中,但它在许多地方被隐式使用。
首先,当调用 filter
或匿名子程序时,将自动创建 $_
的本地副本。此时它将始终包含空字符串。
接下来,filter_read
和 filter_read_exact
都将把读取的任何源数据追加到 $_
的末尾。
最后,当 filter
或匿名子程序完成处理后,它们将使用 $_
返回经过过滤的源。
隐式使用 $_
极大地简化了过滤器。
用户 filter
方法或匿名子程序返回的状态值以及 filter_read
和 read_exact
函数采用相同的值集,即
< 0 Error
= 0 EOF
> 0 OK
过滤器使用这些函数从链中的下一个过滤器(如果没有其他过滤器,则使用实际源文件)获取一行或一个块。
filter_read
函数采用两种形式
$status = filter_read() ;
$status = filter_read($size) ;
第一种形式用于请求行,第二种形式请求块。
在行模式下,filter_read
会将下一行源代码追加到 $_
标量的末尾。
在块模式下,filter_read
会将一个数据块追加到 $_
标量的末尾,该数据块小于等于 $size
。重要的是要强调,filter_read
并不一定会读取一个恰好为 $size
字节的块。
如果您需要能够读取具有确切大小的块,则可以使用函数 filter_read_exact
。它的工作方式与块模式下的 filter_read
完全相同,只是它将尝试读取长度恰好为 $size
字节的块。它不会返回长度为 $size
字节的块的唯一情况是 EOF 或错误。
在每次调用 filter_read
或 filter_read_exact
之后,检查 $status
的值非常重要。
函数 filter_del
用于禁用当前过滤器。它不影响过滤器的运行。它所做的只是告诉 Perl 不要再调用过滤器。
有关详细信息,请参见 "示例 4:使用 filter_del"。
内部函数,它根据 filter_add 参数类型添加过滤器。
可用于禁用过滤器,但很少需要。请参见 filter_del。
有关仅在文本行级别过滤代码的一般问题的概述,请参见 perlfilter 中的“限制”。
不会过滤 __DATA__ 块中的内容。这是一个严重的限制,例如对于 Switch 模块。有关更多信息,请参见 http://search.cpan.org/perldoc?Switch#LIMITATIONS。
当前内部缓冲区长度仅限于 32 位。
这里有一些示例来说明关键概念 - 因此其中大多数在实际中用处不大。
examples
子目录包含所有这些过滤器作为方法过滤器和闭包过滤器实现的副本。
下面是一个方法过滤器,它被硬编码为将字符串 "Joe"
的所有出现替换为 "Jim"
。并不是特别有用,但这是第一个示例,我想保持简单。
package Joe2Jim ;
use Filter::Util::Call ;
sub import
{
my($type) = @_ ;
filter_add(bless []) ;
}
sub filter
{
my($self) = @_ ;
my($status) ;
s/Joe/Jim/g
if ($status = filter_read()) > 0 ;
$status ;
}
1 ;
下面是使用该过滤器的示例
use Joe2Jim ;
print "Where is Joe?\n" ;
以上脚本将打印以下内容
Where is Jim?
前面的示例并不是特别有用。为了使其更通用,我们将使用上下文数据,并允许使用任意从和到字符串。这次我们将使用闭包过滤器。为了反映其增强后的角色,该过滤器称为 Subst
。
package Subst ;
use Filter::Util::Call ;
use Carp ;
sub import
{
croak("usage: use Subst qw(from to)")
unless @_ == 3 ;
my ($self, $from, $to) = @_ ;
filter_add(
sub
{
my ($status) ;
s/$from/$to/
if ($status = filter_read()) > 0 ;
$status ;
})
}
1 ;
并像这样使用
use Subst qw(Joe Jim) ;
print "Where is Joe?\n" ;
这是一个筛选器,是 Joe2Jim
筛选器的变体。除了将所有出现的 "Joe"
替换为 "Jim"
之外,它还记录在上下文对象中进行的替换次数。
一旦检测到 EOF($status
为零),筛选器就会在源流中插入一行额外的行。执行此额外行时,它将打印实际进行的替换次数。请注意,在这种情况下,$status
设置为 1
。
package Count ;
use Filter::Util::Call ;
sub filter
{
my ($self) = @_ ;
my ($status) ;
if (($status = filter_read()) > 0 ) {
s/Joe/Jim/g ;
++ $$self ;
}
elsif ($$self >= 0) { # EOF
$_ = "print q[Made ${$self} substitutions\n]" ;
$status = 1 ;
$$self = -1 ;
}
$status ;
}
sub import
{
my ($self) = @_ ;
my ($count) = 0 ;
filter_add(\$count) ;
}
1 ;
这是一个使用它的脚本
use Count ;
print "Hello Joe\n" ;
print "Where is Joe\n" ;
输出
Hello Jim
Where is Jim
Made 2 substitutions
主题的另一个变体。这一次,我们将修改 Subst
筛选器,以允许指定开始和停止模式以及 from 和 to 模式。如果你了解 vi 编辑器,它相当于此命令
:/start/,/stop/s/from/to/
当用作筛选器时,我们希望像这样调用它
use NewSubst qw(start stop from to) ;
以下是模块。
package NewSubst ;
use Filter::Util::Call ;
use Carp ;
sub import
{
my ($self, $start, $stop, $from, $to) = @_ ;
my ($found) = 0 ;
croak("usage: use Subst qw(start stop from to)")
unless @_ == 5 ;
filter_add(
sub
{
my ($status) ;
if (($status = filter_read()) > 0) {
$found = 1
if $found == 0 and /$start/ ;
if ($found) {
s/$from/$to/ ;
filter_del() if /$stop/ ;
}
}
$status ;
} )
}
1 ;
如果你打算使用 Filter::Call 功能,我强烈建议你查看 Damian Conway 出色的 Filter::Simple 模块。Damian 的模块提供了比 Filter::Util::Call 更简洁的界面。虽然它不允许 Filter::Util::Call 所做的精细控制,但它应该足以满足大多数应用程序。它可在以下位置获得
http://search.cpan.org/dist/Filter-Simple/
Paul Marquess
1996 年 1 月 26 日
版权所有 (c) 1995-2011 Paul Marquess。保留所有权利。版权所有 (c) 2011-2014、2018-2022 Reini Urban。保留所有权利。版权所有 (c) 2014-2017 cPanel Inc。保留所有权利。
本程序是免费软件;你可以在与 Perl 自身相同的条款下重新分发和/或修改它。