使用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函数都有效,例如seektruncatesysread等。

参见

《perlfunc(1)》中的 open 函数和《Programming Perl, 3rd Edition》第29章(“函数”)中;以及“使用随机访问I/O”和“设置默认I/O层”


O'Reilly & Associates 将很快发布(2003年8月)第二版 《Perl Cookbook》。


标签

反馈

这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们。