使用Perl烹饪
编辑注:新的《Perl食谱》第二版即将上市,为了庆祝其发布,我们提供了一些新食谱,供您品尝。本周的摘录包括第6章(“模式匹配”)和第8章(“文件内容”)的食谱。并且请记住,在接下来的几周内,这里还将有更多关于如何在不使用数据库服务器的情况下使用SQL、提取表格数据、使用HTML::Mason进行模板化等主题的新食谱。
示例食谱:匹配嵌套模式
问题
您想匹配一组嵌套括号,例如函数调用的参数。
解决方案
使用匹配时模式插值,递归地
my $np;
$np = qr{
\(
(?:
(?> [^( )]+ ) # Non-capture group w/o backtracking
|
(??{ $np }) # Group with matching parens
)*
\)
}x;
或使用Text::Balanced模块的extract_bracketed
函数。
讨论
$(??{
CODE})
结构运行代码并将代码返回的字符串插回到模式中。一个简单的非递归示例,匹配回文,展示了这一点
if ($word =~ /^(\w+)\w?(??{reverse $1})$/ ) {
print "$word is a palindrome.\n";
}
考虑一个像“reviver”这样的词,该模式正确地报告它是一个回文。在匹配过程中,$1
变量包含"rev"
部分。然后可选的单词字符捕获"i"
。然后运行代码reverse $1
并产生"ver"
,该结果被插回到模式中。
对于匹配平衡的内容,您需要递归,这有点棘手。使用(??{
CODE})
的编译模式可以引用自身。解决方案中给出的模式匹配一组嵌套括号,无论它们有多深。在该模式中,给定$np
的值,您可以使用它来匹配一个函数调用
$text = "myfunfun(1,(2*(3+4)),5)";
$funpat = qr/\w+$np/; # $np as above
$text =~ /^$funpat$/; # Matches!
您会发现许多CPAN模块有助于匹配(解析)嵌套字符串。Regexp::Common模块提供了匹配许多复杂字符串的现成模式。例如
use Regexp::Common;
$text = "myfunfun(1,(2*(3+4)),5)";
if ($text =~ /(\w+\s*$RE{balanced}{-parens=>'( )'})/o) {
print "Got function call: $1\n";
}
该模块提供的其他模式匹配各种记号的数字和引号分隔的字符串
$RE{num}{int}
$RE{num}{real}
$RE{num}{real}{'-base=2'}{'-sep=,'}{'-group=3'}
$RE{quoted}
$RE{delimited}{-delim=>'/'}
标准(截至v5.8)Text::Balanced模块为这个问题提供了一个通用解决方案。
use Text::Balanced qw/extract_bracketed/;
$text = "myfunfun(1,(2*(3+4)),5)";
if (($before, $found, $after) = extract_bracketed($text, "(")) {
print "answer is $found\n";
} else {
print "FAILED\n";
}
参见
《Programming Perl,第3版》中第5章“模式匹配”关于“匹配时模式插值”的部分;Regexp::Common CPAN模块和标准Text::Balanced模块的文档。
示例食谱:假装字符串是文件
问题
您在字符串中有数据,但希望将其作为文件处理。例如,您有一个期望文件句柄作为参数的子例程,但您希望该子例程直接在您的字符串数据上工作。此外,您不想将数据写入临时文件。
解决方案
使用Perl v5.8的标量I/O
open($fh, "+<", \$string); # read and write contents of $string
讨论
Perl的I/O层包括从标量进行输入和输出的支持。当您使用<$fh>
读取记录时,您正在从$string
中读取下一行。当您使用print
写入记录时,您会更改$string
。您可以传递$fh
到期望文件句柄的函数,并且该子例程永远不会知道它实际上正在处理字符串中的数据。
Perl尊重字符串的open
中的各种访问模式,因此您可以指定将字符串以只读、截断、追加模式等打开
open($fh, "<", \$string); # read only
open($fh, ">", \$string); # write only, discard original contents
open($fh, "+>", \$string); # read and write, discard original contents
open($fh, "+<", \$string); # read and write, preserve original contents
这些句柄在所有方面都像常规文件句柄一样表现,因此所有I/O函数都有效,例如seek
、truncate
、sysread
等。
参见
《perlfunc(1)》中的 open
函数和《Programming Perl, 3rd Edition》第29章(“函数”)中;以及“使用随机访问I/O”和“设置默认I/O层”
O'Reilly & Associates 将很快发布(2003年8月)第二版 《Perl Cookbook》。
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们。