以下为引用的内容: #!/usr/bin/perl -w # MetaphoneSuggest - suggest links for typographical and other errors from 404s use strict; use CGI::Pretty ':standard'; #standard cgi stuff use Text::Metaphone;
my @suggestLinks = (); # suggested link list my %mt = (); # filename, score, metaphone code hash my $origLink = substr($ENV{REDIRECT_URL},1); # remove leading / $origLink =~ s//.html//g; # remove trailing .html open(MPH,'metaphonesScore.txt') or die can't open metaphones while(my @slPart = split '###', MPH ) { $slPart[0] =~ s/ //g; #remove trailing space $mt{$slPart[0]}{ score } = $slPart[1]; $mt{$slPart[0]}{ metaphones } = $slPart[2]; } close(MPH); 代码首先引入了一些常用库并声明了一些变量,然后将加载 404 报告文本和通过 buildMetaphoneList.pl 程序创建的变音。这时,我们可以开始编写主要的程序逻辑了,如下所示。清单 5. 主要程序逻辑
以下为引用的内容: push @suggestLinks, sortResults( directorySplitTest( $origLink ) ); push @suggestLinks, sortResults( combinedTest( $origLink ) ); push @suggestLinks, sortResults( containsTest( $origLink ) ); # from the book - unique-ify the array my %seen = (); @suggestLinks = grep{ ! $seen{$_}++ } @suggestLinks ; print header; print qq{Error 404: The file requested [$ENV{REDIRECT_URL}] is unavailable. BR next if( @suggestLinks == 0 ); print qq{Please try one of the following pages: BR for my $link( @suggestLinks ){ $link = substr($link,index($link,'./')+1); print qq{ a href= $link $link /a BR } 首先,对匹配测试各部分的输出进行排序,然后将其添加到总建议链接列表。对链接列表进行排序和惟一化(unique-ifying)之后,将建议链接直接打印输出。三个排序命令将结果保存在同一个数组中,目的是创建一个有序的建议列表。发生 404 错误时,目录树中(至少第一级目录)极有可能会出现目录分隔符(用于表示 Web 页面)。比如说,以 bloggs/nathenherringtoon.html 页面请求为例。上述代码中所调用的 directorySplitTest 方法将创建一个排序的页面列表,BLKS 和子目录 N0NHRNKTN 的变音匹配都将包含在该列表中。这一策略可用于区分根目录中的文件(如 blogs.html 和 nathanharrington.html)和完整路径名匹配的页面(如 blogs/nathanharrington.html)。下面的清单显示了 directorySplitTest 子例程的内容。清单 6. directorySplitTest subroutine
以下为引用的内容: sub directorySplitTest { my @matchRes = (); my $inLink = $_[0]; for my $fileName ( keys %mt ) { my @inLinkMetas = (); # process each metaphone chunk as a directory for my $inP ( split '//', $inLink ){ push @inLinkMetas, Metaphone($inP) } my @metaList = split ' ', $mt{$fileName}{metaphones}; next if( @metaList != @inLinkMetas ); my $pos = 0; my $totalMatch = 0; for( @metaList ) { $totalMatch++ if( $metaList[$pos] =~ /(/b$inLinkMetas[$pos]/b)/i ); $pos++; }#for meatlist # make sure there is a match in each metaphone chunk next if( $totalMatch != @metaList ); push @matchRes, $mt{$fileName}{score} ## $fileName }#for keys in metaphone hash return( @matchRes ); }#directorySplitTest 组合测试位于 directorySplitTest 之后,用于检查变音混和在一起时的匹配情况 忽略任何目录结构。该测试用于纠正 404 类错误,即文件名中含有空格、斜杠、反斜杠、冒号和其他一些无发音的字符。比如说,如果针对 blogs_nathanherrington.html 发出一个 404 请求,那么 directorySplitTest 将返回零结果,但是 combinedTest 将发现该 404 产生的变音组合在一起是 blogs/NathanHarrington.html 页面的准确匹配。同样,这些建议的优先级低于目录匹配,因此这些分类结果将在 directorySplitTest 之后存入 suggestLinks 数组。以下清单显示了 combinedTest 子例程。清单 7. combinedTest 子例程
以下为引用的内容: sub combinedTest { my @matchRes = (); my $inLink = $_[0]; for my $fileName ( keys %mt ) { my $inLinkMeta = Metaphone($inLink); # smoosh all of the keys together, removing spaces and trailing newline my $metaList = $mt{$fileName}{metaphones}; $metaList =~ s/( |/n)//g; next if( $metaList !~ /(/b$inLinkMeta/b)/i ); push @matchRes, $mt{$fileName}{score} ## $fileName }#for filename keys in metaphone hash return(@matchRes); }#combinedTest 在 combinedTest 之后是最后一个匹配测试,该测试基于一个广度包含搜索。如果当前的 404 链接的变音是 metaphoneScores.txt 文件中可用变音的一部分,我们将把它添加到建议列表。包含搜索的设计目的是寻找内容极度不完整的 URL。nathan.html 页面在任何位置都无法找到,但是一个良好的建议应该是 /NathanHarrington.html 和 /blogs/NathanHarrington.html,并且它们根据作用域值排序并添加到 suggestLinks 数组中。注意,此方法还将为单字母变音 404(如 whoo.html)生成 NathanHarrington.html 建议。由于 NathanHarrington.html 变音中含有一个 H ,故将其添加到建议列表。考虑创建一个最小长度的匹配变音,或提供一个包含总数受限的匹配,以修改这一行为。清单 8 显示了 containsTest 和 sortResults 子例程。清单 8. sortResults 和 containsTest 子例程
以下为引用的内容: sub sortResults { # simply procedue to sort an array of 'score ## filename' entries my @scored = @_; my @idx = (); #temporary index for sorting for my $entry( @scored ){ # create an index of scores my $item = substr($entry,0,index($entry,'##')); push @idx, $item; }
# sort the index of scores my @sorted = @scored[ sort { $idx[$b] = $idx[$a] } 0 .. $#idx ];
return( @sorted );
}#sortResults sub containsTest { my @matchRes = (); my $inLink = $_[0]; for my $fileName ( keys %mt ) { my $inLinkMeta = Metaphone($inLink); my $metaList = $mt{$fileName}{metaphones}; next if( $metaList !~ /$inLinkMeta/i ); push @matchRes, $mt{$fileName}{score} ## $fileName }#for filename keys in metaphone hash return(@matchRes); }#containsTest 修改 Apache httpd.conf 文件上面所设计的 MetaphoneSuggest 脚本是一个将从 Apache 中直接调用的 cgi-bin 脚本。要运行 MetaphoneSuggestscript 脚本,我们需要对 httpd.conf 文件进行适当修改,否则将显示 404 错误页面。比如说,如果默认的 httpd.conf 文件含有以下部分:清单 9. 默认 httpd.conf 部分
以下为引用的内容: # Customizable error responses come in three flavors: # 1) plain text 2) local redirects 3) external redirects # # Some examples: #ErrorDocument 500 The server made a boo boo. #ErrorDocument 404 /missing.html #ErrorDocument 404 /cgi-bin/missing_handler.pl #ErrorDocument 402 http://www.example.com/subscription_info.html 在注释掉的 ErrorDocument 代码行之后插入如下代码:ErrorDocument 404 /cgi-bin/MetaphoneSuggest 。确保 MetaphoneSuggest 和 metaphonesScore.txt 文件位于 Web 服务器的 document_root /cgi-bin/ 目录下。以根用户身份发起服务器重启命令:例如 /usr/local/apache2/bin/apachectl restart,至此灵活的建议机制将彻底结束笨拙的 404 错误。结束语