Perl食谱1

取自 PerlChina.org - wiki

跳转到: 导航, 搜索

Perl食谱1


目录


编者的话:最新版的 Perl Cookbook 马上就要放上书店的书架开始发售了,这里我们介绍一些第二版里新增的内容,你可以先看看这些节选的内容。这周我们要介绍的是从第六章“模式匹配”和第八章“文件操作”中节选的内容。一定记住下周还来这个地方哦,我们会介绍脱离数据库来使用 SQL 语言,从表格中提取数据,用 HTML::Mason 的模板技术进行网页模板编程等。

[编辑] 技巧摘录:匹配多重嵌套的模式

[编辑] 提出问题

你想匹配由多重定界符构成的嵌套模式,比如一个函数的参数。

[编辑] 解决方案

层层匹配,每层匹配后把该层的模式替换为匹配的值

my $np;
$np = qr{
      \(
      (?:
       (?> [^( )]+ )  # Non-capture group w/o backtracking
      |
       (??{ $np })   # Group with matching parens
      )*
      \)
    }x;

或者直接使用 Text::Balanced 模块的 extract_bracketed 函数。

[编辑] 讨论

$(??{_CODE_}) 结构将运行 CODE 所代表的代码,然后用得到的结果来替换模式,一个简单的,不带递归的的匹配回文结构的例子如下:(注:回文结构就是一个词的构成具有前后对称性,如“deed”,“peoep”)

if ($word =~ /^(\w+)\w?(??{reverse $1})$/ ) {
  print "$word is a palindrome.\n";
}

考虑 “reviver” 这个单词,他将会作为回文被匹配。$1 内置变量在匹配中保存 “rev”。后面跟任意“一个”字符 ,此时为”。然后 reverse $1 语句产生“ver”字符串,并且插入 reverse $1 语句所在位置来进行匹配。

要匹配一些均衡的模式,你需要用到递归,递归方法有一些技巧性。一个包含 (??{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";
}

Regexp::Common 提供的其它模模式匹配各种不同标记法中的数字和引起来的字符串:

$RE{num}{int}
$RE{num}{real}
$RE{num}{real}{'-base=2'}{'-sep=,'}{'-group=3'}
$RE{quoted}
$RE{delimited}{-delim=>'/'}

标准的 Text::Balanced 模块在(5.8版)则提供一个统一方法匹配那些数字和字符串:

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 3rd Edition”一书第5章中的“Pattern Matching”一节;CPAN 上 Regexp::Common 模块和标准 Text::Balanced 模块的相关文档

摘录人:Tome Christiansen,Nathan Torkington

[编辑] 技巧节选:把字符串当作文件

[编辑] 提出问题

现在你手头上有的是字符串,但是你想把它作为一个文件来处理。例如,有一个子函数,它的参数为文件句柄,但是你想这个函数直接作用于你的字符串,而你不想把字符串写入一个文件。

[编辑] 解决方案

用 Perl 5.8 版中的 I/O 标量:

open($fh,"+<",\$string); # 从 $string 中读取数据

[编辑] 讨论

Perl 的输入输出层支持把数据存入标量及从标量中取出数据。像上面,当你用 <$fh> 语句读取一条记录的时候,你读的是 $string 中所存储数据的下一行。当你用 print 函数向 $string 中写入数据时,你会改变 $string 中现有的数据。你可以把 $fh 传递给一个参数为文件句柄的函数,而那个函数将不会知道它实际在处理字符串而不是文件。

Perl 也能以各种不同的方式来打开字符串,所以,你可以用只读、覆盖、追加等方式打开:

open($fh, "<", \$string);  # 只读方式打开字符串
open($fh, ">", \$string);  # 先清空字符串,然后字符串只写
open($fh, "+>", \$string);  # 先清空字符串,字符串可读可写
open($fh, "+<", \$string);  # 字符串可读可写,但是写的时候添加在已有内容的后面

这些返回的句柄完全和文件句柄一样,所以所有针对文件的 I/O 操作都能继续用,例如 seek,truncate,sysread 和 friends。

[编辑] 建议阅读

“Programming Perl,3rd Edition”一书第29章 perlfunc(1) 中的对 open 函数的介绍;“Using Random-Access I/O” 和“Setting the Default I/O Layers” 这两篇文章。

个人工具