« 附錄A. 習題解答 | Perl 學習手札目录

附錄B:常用的五十個CPAN模組

附錄B:常用的五十個CPAN模組

這個附錄的內容原來是來自於Autrijus Tang(唐宗漢)的「CPAN模組數來寶」,正如我們說的,使用Perl而不使用CPAN實在是無法感覺到那種驚人的集結力量。可是CPAN模組數量之多,要能從中得到一個適合自己的模組,也有相當的難度。因此能夠以一般最常使用的模組來作為熟悉CPAN的開始,確實是美事一樁。而這份投影片曾經在許多地方發表過,包括台北的「Perl/PHP/Python Party」、德國的「German Perl Workshop」,以及美國的「Open Source Conference 2003」發表過,也都獲得很好的迴響。所以我就決定把這一份投影片改寫為這本書的附錄。

CPANPLUS

我們前面提過好幾次關於CPANPLUS,因為這個模組很可能在接下來的Perl版本中,將接替CPAN成為預設的模組安裝管理工具。

% cpanp
a # 依作者名稱搜尋模組
m # 依模組名稱搜尋模組
f # 列出作者的所有套件
o # 列出可供更新的模組
i # 安裝
t # 測試
u # 移除
d # 下載
l # 詳細資訊
r # 顯示README
c # 品管報告
z # 解開模組

LWP::Simple

利用LWP::Simple,可以很容易的讓我們在Perl程式中取得某個網頁的內容,這經常被拿來運用於資料的收集。

$page = get("http://www.cpan.org/"); # 取得網頁
getprint($url); # 印出內容
getstore($url, $file); # 存入檔案
head($url) # 取得標頭
mirror($url, $file); # 映射網址

WWW::Mechanize

如果你每天都要進行相同的步驟去登入某個網站,或填入某些資料取得相關的資訊,那麼你可以可以透過WWW::Mechanize來請Perl幫忙。他可以像一個機器人般的,幫你進行這些繁雜的手續,就像你的代理人一般。

my $agent = WWW::Mechanize->new(); # 建立物件
$agent->get($url); # 到達網站
$agent->follow($link); # 按下鏈結
$agent->form($number); # 進入表單
$agent->field($name, $value); # 輸入資料
$agent->click($button); # 按下按鈕
$agent->back(); # 回上一頁
$agent->add_header($name => $value); # 加入標頭
print $agent->{content}; # 印出結果

HTML::Mason

我們在正文中也提過這個模組,他可以讓我們簡單的寫出動態的網站,就像你在寫HTML一樣。如果你熟悉PHP,你應該也會很習慣這個方式,當然,他其實是非常強力的內嵌式模板系統。

<%perl>
my $noun = '全世界'; # 內嵌程式碼
my @time = split /[\s:]/, localtime;

歡迎<% $noun %>, # 安插運算式
% if ( $time[3] < 12 ) { # 流程控制
早安!
% } else {
晚安!
% }

Template

另一個在Perl領域中常用的模板系統,他可以很方便的讓你切開頁面設計跟程式碼,讓兩者的相關性減到最低。也讓所有人都可以在不互相干擾的情況下發揮比較大的效率。

[% INCLUDE header title = 'This is an HTML example' %]

Some Interesting Links


[% webpages = [ # 內嵌程式碼
{ url => 'http://foo.org', title => 'The Foo Organsiation' }
{ url => 'http://bar.org', title => 'The Bar Organsiation' }
] %]

    [% FOREACH link = webpages %] # 流程控制
  • [% link.title %] # 取得元素
    [% END %]

[% INCLUDE footer %] # 套用元件

XML::RSS

要怎麼處理RSS的檔案?XML::RSS是一個非常方便的工具。你可以透過簡單的處理,產生出一個標準的RSS。

my $rss = XML::RSS->new(); # 建立物件
$rss->parse($string); # 剖析字串
foreach my $item (@{$rss->{'items'}}) { # 處理元素
print "title: $item->{'title'}\n";
print "link: $item->{'link'}\n\n";
}
$rss->add_module( # 自訂模組
prefix => 'content', # 全文模組
uri => 'http://purl.org/my/rss/module/',
);
$rss->add_item( # 新增元素
title => $title,
link => $link,
content => { encoded => $text },
);
$rss->{output} = '1.0'; # 轉換版本
print $rss->as_string; # 印出 XML

DBI

DBI幾乎是現在寫程式必備的模組之一了,當然他本身也已經是一種標準。因此了解DBI的使用方式顯然是一個重要的課題。

my $dbh = DBI->connect( # 連結資料庫
"DBI:mysql:database=test;host=localhost",
"Melody", "Nelson", {'RaiseError' => 1}
);
eval { $dbh->do("DROP TABLE foo") }; # 卸除資料庫
# 建立資料庫
$dbh->do("CREATE TABLE foo (id INTEGER, name VARCHAR(20))");
# 插入資料列(用 quote 進行引括)
$dbh->do("INSERT INTO foo VALUES (1, " . $dbh->quote("Tim") . ")");
# 插入資料列(用 ? 進行引括)
$dbh->do("INSERT INTO foo VALUES (?, ?)", undef, 2, "Jochen");
my $sth = $dbh->prepare("SELECT * FROM foo"); # 準備選取資料列
$sth->execute; # 執行選取
while (my $ref = $sth->fetchrow_hashref()) { # 選取成雜湊
print "Found a row: id = $ref->{'id'}, name = $ref->{'name'}\n";
}
$sth->finish; # 結束查詢
$dbh->disconnect; # 結束資料庫連線

YAML

我們都知道在Perl中有陣列,雜湊,或陣列的陣列,雜湊的雜湊,或雜湊的陣列,雜湊的雜湊......,這麼複雜的資料結構,透過YAML可以讓我們清楚的一目了然。這樣才不會讓程式設計師到最後自己都搞不清楚資料的結構到底是甚麼樣了。

# 將複雜的資料結構傾印成跨平台、跨語言、簡潔易讀的文件格式
print Dump { 'P3P' => {
'Date' => [ '2003-02-07T10:00:00', '2003-02-09T12:00:00' ],
'Entry Fee' => 'USD$6',
'Hosted-By' => 'Taipei Perl Mongers',
'URL' => \('http://p3p.elixus.org/'),
} };

# 結果如下, 比 XML 漂亮多了吧. :-)
--- #YAML:1.0
P3P:
Date:
- 2003-02-07T10:00:00
- 2003-02-09T12:00:00
Entry Fee: USD$6
Hosted-By: Taipei Perl Mongers
URL: !perl/ref:
=: http://p3p.elixus.org/

Storable

在Perl中,怎麼簡單的儲存資料。如果你只想單純的把一些資料記下,又不想勞師動眾的安裝巨大的資料庫,還要煩惱資料庫的規劃。隨手使用Storable也許是個不錯的解決方式。

store \%table, 'file'; # 傾印 \%table 到二進制檔案 file
$hashref = retrieve('file'); # 讀回 \%table
nstore \%table, 'file'; # 跨平台的傾印格式
$hashref = retrieve('file'); # 相同的讀法

store_fd \@array, \*STDOUT; # 存進檔案代號
nstore_fd \%table, \*STDOUT; # 存進檔案代號(跨平台)
$aryref = fd_retrieve(\*SOCKET); # 從網路 socket 讀取
$hashref = fd_retrieve(\*SOCKET); # 從網路 socket 讀取

$serialized = freeze \%table; # 存進純量變數中
%clone = %{ thaw($serialized) }; # 解開成等價的雜湊
$cloneref = dclone($ref); # 也可以這樣寫

lock_store \%table, 'file'; # 非強制式鎖定寫入
lock_nstore \%table, 'file'; # 同上(跨平台)
$hashref = lock_retrieve('file'); # 非強制式鎖定讀取

BerkeleyDB

當然,你也可以使用簡單的資料庫來儲存,而這時候BerkeleyDB就是一個很好的選擇。我們也在內文中提過這個部份,在這裡大家可以作為一個備忘。

tie my %h, "BerkeleyDB::Hash", # 繫結 %h 雜湊
-Filename => 'test.db', # 連到 test.db 資料庫
-Flags => DB_CREATE, # 若不存在,即行建立
or die "$BerkeleyDB::Error: $!\n" ;

# 將鍵/值對加入檔案中
$h{"蘋果"} = "紅";
$h{"柳橙"} = "橙";
$h{"香蕉"} = "黃";
$h{"蕃茄"} = "紅";
# 檢查某個鍵存在與否
print "香蕉船!\n\n" if $h{"香蕉"};
# 刪除鍵/值對
delete $h{"蘋果"};
# 印出資料檔的內容
while (my ($k, $v) = each %h) { print "$k -> $v\n" }
# 解除繫結
untie(%h);

Inline::Files

當然,還有神奇的方式來儲存資料,也就是儲存在程式本身。例如我們以前常看到的網站計數器,既然我們儲存的只是一個整數,那麼把他儲存在程式中也是一個不錯的方法,而Inline::Files就可以達到這樣的效果。

# 簡單的計數器
open COUNT or die $!; # 開啟虛擬檔案 __COUNT__
my $count = ; # 讀進目前紀錄
open COUNT, ">$COUNT" or die $!; # 撰寫虛擬檔案 __COUNT__
print COUNT ++$count; # 寫入新的值
open DATE, ">$DATE" or die $!; # 撰寫虛擬檔案 __DATE__
print DATE scalar localtime; # 寫入更新日期

__COUNT__
1
__DATE__
Sat Feb 8 11:01:33 CST 2003

Devel::DProf

如果你打算為你的程式進行最佳化,那麼檢查程式的執行時間是有必要的,你可以發現程式的瓶頸是在那裡發生的。並且判斷每個部份的重要性以及對整體效率的影響。

# 執行效能分析, 寫入紀錄檔 tmon.out
% perl -d:DProf /usr/local/bin/cpanp -m Foobar
# 分析紀錄檔, 印出效能報表
% dprofpp
Total Elapsed Time = 5.074083 Seconds # 總執行時間
User+System Time = 4.053218 Seconds # 實際使用時間
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
40.4 1.641 1.641 2 0.8203 0.8203 Storable::net_pstore
28.3 1.148 1.148 3 0.3828 0.3828 Storable::pretrieve
5.97 0.242 0.241 7 0.0346 0.0345 CPANPLUS::Internals::Search::BEGIN
4.59 0.186 0.271 14 0.0133 0.0194 CPANPLUS::Configure::Setup::BEGIN
2.66 0.108 0.179 23 0.0047 0.0078 CPANPLUS::Internals::BEGIN

Inline

常常有人抱怨用Perl寫出來的程式效率比較差,如果你把Perl拿來跟C或甚至組合語言相比,那麼結果大概很容易想像。可是好消息是你可以把程式中最強調效率的部份用C來寫,而且你確實可以在Perl裡面寫C,只要你用上Inline模組。不單單只是C,Inline還包含了Java,Assembley或各式各樣的其他程式語言。讓大家都能夠得心應手的把各種語言跟Perl「黏」在一起。

# 內嵌 C 語言函式
use Inline C => 'void greet(char *x) { printf("Hello, %s!\n", x); }';
greet("World"); # 印出 "Hello, World!\n"

# 內嵌 Python 函式
use Inline Python => '
def Foo():
class Bar:
def __init__(self):
print "new Bar()"
def tank():
return 10
return Bar()
';

my $o = Foo(); # 建立物件
print $o->tank; # 印出 10

# 還支援組合語言、Awk、BC、Basic、Befunge、C++、Guile、Java、Ruby、Tcl...

Locale::Maketext::Lexicon

程式的國際化(i18n)某種程度而言是非常必要的,因此藉由Locale::Maketext::Lexicon就可以幫我們處理一大堆的瑣事。接下來只要有完整的語系檔,就可以讓應用程式說出各式各樣的語言。

% xgettext.pl *.pl # 將目錄下所有 .pl 檔內的可譯詞解到 messages.po 內

# 撰寫國際化程式的好工具
use base 'Locale::Maketext'; # 採用 Maketext 本土化架構
use Locale::Maketext::Lexicon { # 定義詞典檔
en => [ 'Auto' ], # 以英文為基底語言
de => [ Gettext => 'de.po' ], # 從 de.po 讀入德文詞典
fr => [ Tie => [ DB_File => 'fr.db' ]], # 從 fr.db 讀入法文詞典
zh_tw => [ Gettext => \*DATA ], # 從 __DATA__ 讀入中文詞典
};
my $h = __PACKAGE__->get_handle; # 自動取得使用者語系
print $h->maketext("Hello, [_1]!", "Perl"); # 印出本土化的訊息
__DATA__
msgid "Hello, %1!"
msgstr "%1 您好!"

Log::Dispatch

程式執行時經常都會有意外發生,或是程式本身的錯誤,還是操作上的問題。而要排除這些問題,最好的方式就是在意外發生時能紀錄下意外發生的狀況。而且還可以在有重大的意外狀況時通知管理者儘速排除。

my $log = Log::Dispatch->new; # 建立紀錄物件
$log->add( Log::Dispatch::File->new( # 新增紀錄檔物件
name => 'file', # 物件名稱
min_level => 'debug', # 紀錄門檻
filename => '/var/log/test.log', # 紀錄檔名
) );
$log->add( Log::Dispatch::Email::MailSend->new( # 新增郵件紀錄物件
name => 'email', # 物件名稱
min_level => 'emergency', # 紀錄門檻
to => [ qw( foo@bar.com bar@baz.org ) ], # 收件地址
subject => '救命啊!!!', # 郵件標題
) );
$log->info("系統啟動中..."); # 存到紀錄檔裡
$log->error("磁碟空間不足..."); # 同上
$log->emergency("記憶體損毀!"); # 送出 Email

Test::More

測試當然是寫程式重要的過程跟檢驗之一。而如果能有方便的工具讓程式設計師不用煩惱怎麼做測試,那應該會提昇不少工作效率。這時後用Test::More應該是很好的選擇。

use Test::More tests => 16; # 測試數量
use_ok('CGI'); # 匯入模組
require_ok('Test::More'); # 使用模組
ok( "空" eq "空", '空即是空' ); # 真值檢查
is( "色", "色", '色即是色' ); # 字串相等
isnt( "空", "色", '空不是色'); # 字串不等
isn't("色", "空", '色不是空'); # 字串不等
like("空空", '/^空/', '空空如也'); # 字串比對
isa_ok(CGI->new, 'CGI', '物件類別'); # 物件類別
eq_array([1..3], [1..3], '陣列相等'); # 陣列相等
cmp_ok(1+1, '==', 2, '數值相等'); # 數值相等
is_deeply($ref1, $ref2, '複雜結構'); # 複雜結構
can_ok('Test::More', qw(ok is isnt like skip), '方法測試');

Regexp::Common

正規表示式確實是非常方便的工具,可是有些時候要寫出一個好的正規表示式的樣式確實非常讓人困擾的。幸好,很多情況下我們都會寫出類似的樣式,而這些樣式其實也有不少人曾經使用,我們就可以利用Regexp::Common來方便的使用這些非常一般化的樣式了。

while (<>) {
/$RE{num}{real}/ and print "內有數值: $&";
/$RE{profanity}/ and print "不雅文字: $&";
/$RE{quoted}/ and print "引號括住的字串: $&";
/$RE{delimited}{-delim=>'/'}/ and print "斜線括住的字串: $&";
/$RE{balanced}{-parens=>'()'}/ and print "對稱括號內字串: $&";
}

Parse::RecDescent

既然Perl對於文字處理的能力這麼的好,當然也有許多人拿他來進行相關的資料處理。以下就是一個處理一般文章的例子。

# 定義文法規則
$lexer = Parse::RecDescent->new(q(
lex: token(s)

token: 'I\b'
| 'see\b'
| 'on\b'
| 'by\b'
| /the\b|a\b/i
| /\w+/
));
# 進行詞彙分析
my $tokens = $lexer->lex('I see a cat on the windowsill by the door!');

Text::Autoformat

文字的格式也是另一種進行文字處理時會遇到的狀況。在Text::Autoformat,你可以設定好希望的格式,然後交由Perl幫忙排版。

# 整段編排、右邊界 50 欄、左右對齊(限英文;中文見 Lingua::ZH::Wrap)
print autoformat(q(
> Now is the Winter of our discontent made glorious Summer by this
> son of York. And all the clouds that lour'd upon our house in the
> deep bosom of the ocean buried.
- this is a very very very very long item.
- this is another very very very very very very long item.
), { all => 1, right => 50, justify => 'full' });

# 印出結果:
> Now is the Winter of our discontent made
> glorious Summer by this son of York. And all the
> clouds that lour'd upon our house in the deep
> bosom of the ocean buried.
- this is a very very very very long item.
- this is another very very very very very
very long item.

Text::Quoted

另一個分析文章的模組,我們常常在電子郵件內容或是討論區的討論串中使用其他人的引言,而如果想要分析出引言的內容,Text::Quoted就提供了這方面的功能。

# 分析引言結構
my $structure = extract(q(
> foo
> # Bar
> baz

quux
));

# 傳回結構如下:
[ [
{ text => 'foo', quoter => '>', raw => '> foo' },
[ { text => 'Bar', quoter => '> #', raw => '> # Bar' } ],
{ text => 'baz', quoter => '>', raw => '> baz' }
],
{ empty => 1 },
{ text => 'quux', quoter => '', raw => 'quux' } ]

XML::SAX



package MyHandler; # 自訂 SAX 處理器
use base 'XML::SAX::Base';
sub start_element {
my ($self, $data) = @_;
# ... 對 $data 元素進行處理 ...
$self->SUPER::start_element($data);
}

package main;
use XML::SAX::ParserFactory;
my $p = XML::SAX::ParserFactory->parser(Handler => MyHandler->new);
$p->parse_uri("foo.xml"); # 利用 MyHandler 剖析 foo.xml
$p->parse_string(""); # 剖析字串
$p->parse_file($fh); # 剖析檔案

GD

Perl也能畫圖,而且可以畫出非常漂亮的各種圖形,只要使用GD模組,就可以畫出各式各樣的圖表。

my $im = GD::Image->newFromPng('1.png'); # 讀取影像
my $blue = $im->colorAllocate(0,0,255); # 定義色彩
$im->arc(50,50,95,75,0,360,$blue); # 畫上橢圓
open IMG, '>:raw', '2.png'; print IMG $im->png; # 儲存影像

my $graph = GD::Graph::bars->new(400, 300); # 新增柱狀圖
my $gd = $graph->plot([1..10], [2..11]); # 填上資料
open IMG, '>:raw', '3.png'; print IMG $gd->png; # 儲存影像

Imager

另外,影像檔案的處理則是透過Imager來進行。你可以用Imager幫圖檔做旋轉,縮圖,調色等等......。

my $img = Imager->new; # 建立物件
$img->open( file => '1.png', type => 'png'); # 讀取影像
$img = $img->scale(scalefactor => 0.5); # 1/2 縮圖
$img = $img->rotate(degrees => 20); # 旋轉圖形
$img->filter(type => 'autolevels'); # 自動調色
$img = $img->convert(preset => 'grey'); # 轉成灰階
$img->write(file => '2.png'); # 儲存影像

my $pie = Imager::Graph::Pie->new; # 新增圓餅圖
my $img = $pie->draw( # 填上資料
data => [qw( 17874757 8146372 1321544 811406 )],
labels => [qw( Apache Microsoft iPlanet Zeus )],
title => 'Netcraft Web Survey',
legend => { valign => 'bottom' },
features => [qw(labelspconly legend dropshadow)],
);
$img->write(file => '3.png'); # 儲存影像

GraphViz

這是利用模組建立起一堆節點的相關關係,並且繪製成圖檔。最有趣的是它可以自動幫忙調整節點的相互位置。

my $g = GraphViz->new; # 建立有向圖

$g->add_node('唭哩岸'); # 新增節點
$g->add_node('敦煌', label => '莫高窟'); # 附帶描述
$g->add_node('沙瓦那');

$g->add_edge('唭哩岸' => '敦煌'); # 新增路徑
$g->add_edge('唭哩岸' => '沙瓦那', label => '遙遠'); # 附帶描述
$g->add_edge('敦煌' => '唭哩岸'); # 雙向路徑

$g->as_png('graph.png'); # 儲存影像

Image::Size

轉換圖檔的長寬,尤其在網路上時更有用,可以有效管控圖檔的檔案大小,而且支援多種檔案格式。

my ($w, $h) = imgsize("test.png"); # 取得圖片長寬
my $size = html_imgsize("test.png"); # 'width="XX" height="YY"'
my @attrs = attr_imgsize("test.png"); # ('-width', 60, '-height', 40)

# 支援格式: GIF JPG XBM XPM PPM PGM PBM XV PNG MNG TIF BMP PSD SWF PCD

Image::Magick

也是一個圖形處理的模組,它可以讀入圖檔,然後進行各種處理,例如縮圖,轉換檔案等等,就像名稱一般,開始變魔術。

my $image = Image::Magick->new;
my $x = $image->Read('girl.png', 'logo.png', 'rose.png'); # 讀入三個影像
$x = $image->Crop(geometry=>'100x100+1"00"+1"00'); # 截成 100x100
$image->Annotate(font=>'kai.ttf', text=>"太神奇了!"); # 加上文字
$x = $image->Write('animation.gif'); # 存成動畫

Mail::Audit

這是郵件的處理模組,可以根據不同的條件來分配郵件,就像郵差在分派郵件一般。

my $m = Mail::Audit->new(emergency=>"~/emergency_mbox"); # 建立物件
$m->pipe("listgate cle") if $mail->from =~ /cle-devel/; # 送到管線
$m->accept("perl") if $mail->from =~ /perl/; # 接進信箱
$m->reject("nospam") if $mail->rblcheck(); # 彈回垃圾
$m->ignore if $mail->subject =~ /boring/i; # 忽略信件
$m->noexit(1); $m->accept("~/Mail/%Y%m"); $m->noexit(0); # 按月彙整
$m->accept; # 其餘接收

Mail::SpamAssassin

處理垃圾郵件,廣告郵件的好幫手,利用一整套的規則可以相當準確的判讀郵件是否為垃圾郵件。常常和Mail::Audit搭配使用,減少煩人的垃圾信。

my $m = Mail::SpamAssassin::NoMailAudit->new; # 虛擬 Mail::Audit
my $spamtest = Mail::SpamAssassin->new; # 建立過濾器
if ($spamtest->check($m)->is_spam) { # 如果是垃圾信...
$status->rewrite_mail; # ...加上說明
$m->accept("trash"); # ...丟進垃圾桶
} else {
$m->accept; # 不然則照常接收
}

# 典型的 Spam 報告如下:
SPAM: Content analysis details: (7.90 hits, 5 required)
SPAM: UNDISC_RECIPS (1.5 points) Valid-looking To "undisclosed-recipients"
SPAM: NO_REAL_NAME (1.3 points) From: does not include a real name
SPAM: HEADER_8BITS (0.4 points) Headers include 3 consecutive 8-bit characters
SPAM: SPAM_PHRASE_00_01 (0.8 points) BODY: Spam phrases score is 00 to 01 (low)
SPAM: HTML_FONT_COLOR_YELLOW (0.4 points) BODY: HTML font color is yellow
SPAM: BIG_FONT (0.3 points) BODY: FONT Size +2 and up or 3 and up
SPAM: HTML_WITH_BGCOLOR (0.3 points) BODY: HTML mail with non-white background
SPAM: DATE_IN_FUTURE_96_XX (0.5 points) Date: is 96 hours or more after Received: date

Mail::Box

簡單讀取信箱的模組,讓你的Perl程式可以代理你去讀取信箱,然後進行必要的處理。

my $mgr = Mail::Box::Manager->new; # 建立物件
my $folder = $mgr->open(folder => $ENV{MAIL}); # 開啟信箱

print $folder->name; # 印出名稱
print $folder->message(0); # 第一封信
$folder->message(3)->delete; # 刪第三封
my $emails = $folder->messages; # 信件數量

foreach ($folder->messages) {...} # 逐封處理
foreach (@$folder) {...} # 同上

# 新增一封郵件
$folder->addMessage(Mail::Box::Message->new(...));

Mail::Bulkmail

大量發送信件的模組,只需要一個存有所有收件人的文字檔,Perl就可以幫忙寄送郵件。

my $bulk = Mail::Bulkmail->new(
"LIST" => "~/my.list.txt", # 地址清單
"From" => 'not_spam@example.com', # 寄件人
"Subject" => "Test message", # 標題
"Message" => "... blah blah ..." # 內文
) or die Mail::Bulkmail->error();

$bulk->bulkmail() or die $bulk->error; # 寄出大宗郵件

----------------------------------------------------------------

Term::ReadKey
*************
#HTML#


ReadMode(4); # 停用控制鍵
1 until defined($key = ReadKey(-1)); # 等使用者按某個鍵
print "使用者按了 $key\n"; # 印出
ReadMode(0); # 恢復控制鍵

($w, $h, $x, $y) = GetTerminalSize(); # 取得終端機大小
SetTerminalSize($w, $h, $x, $y); # 設定終端機大小

----------------------------------------------------------------

Term::ReadLine
**************

my $term = Term::ReadLine->('小算盤'); # 建立物件
my $prompt = "鍵入數學運算式: "; # 提示字串
my $OUT = $term->OUT || \*STDOUT; # 設定輸出

# 每次讀入一列
while ( defined ($_ = $term->readline($prompt)) ) {
my $res = eval($_); # 執行算式
warn $@ if $@; # 檢查錯誤
print $OUT $res, "\n" unless $@; # 印出結果
$term->addhistory($_) if /\S/; # 加入歷史
}

----------------------------------------------------------------

IPC::Run
********

# 執行 ftp, 以 in.txt 的內容鍵入, 輸出到 $out, 錯誤到 $err
run(['ftp'], 'in.txt', \$out, \$err, timeout(10)) or die $?;

# 設定管線 (pipe), 將壓縮後的資料存進 $out
run([qw(tar cf - test.dat)], '|', ['gzip'], \$out);

# ckhung 的 dynagpt 後端
my $h = start( ['gnuplot'], '>pipe', \*GPT_OUT, 'pipe', \*GPT_ERR ) or die $?;

# 互動式的用法
my $h = start([qw(telnet example.org)], \$in, \$out, \$err, timeout(10));

pump($h) until $out =~ /Login:/; # 等待提示字串
$in .= "Melody\n" ; # 鍵入使用者名稱
pump($h) until $out =~ /Password:/; # 等待提示字串
$in .= "Nelson\n" ; # 鍵入密碼
pump($h) until $out =~ /C:\>/; # 等待提示字串
$in .= "dir /w" ; # 鍵入指令
pump($h) until $out =~ /(\d+) bytes/; # 擷取所要的輸出

finish($h) or die $?; # 結束行程
warn $err if $err; # 顯示警告
print $out; # 列印輸出

----------------------------------------------------------------

PAR
***

% pp -o packed.exe source.pl # 自足的 .exe 檔
% packed.exe # 在同樣的作業系統下皆可執行
% pp -o packed.exe -e 'print "Hi!"' # 將單列程式轉成執行檔

% pp -p CGI.par -M CGI # 封裝 CGI.pm 及其所需模組
% parl -ICGI.par script.pl # parl 可直接讀入 .par 程式庫

# 在 httpd.conf 裡, 直接執行 Perl Servlet:

PARDir /path/to/par/archive/directory
PARFile /path/to/a/par/file.par
PerlModule Apache::PAR
...


----------------------------------------------------------------

PPerl
*****

# 執行 cpanp, 結束時將它轉為常駐程式
% time perl /usr/local/bin/cpanp -m Foobar
3.36 real 2.37 user 0.30 sys
# 再次執行時, 可省去啟動及載入模組的時間
# (跟 FastCGI、ModPerl::Registry 原理相同)
% time pperl /usr/local/bin/cpanp -m Foobar
1.02 real 0.44 user 0.20 sys
# 殺掉常駐程式
% pperl -- -k cpanp

----------------------------------------------------------------

Archive::Tar
************
#HTML#


Archive::Tar->create_archive( # 建立壓縮檔
"my.tar.gz", # 檔名
9, # 壓縮程度
("/this/file", "/that/file"), # 來源檔案
);
# 列出其中的檔案
print "$_\n" for Archive::Tar->list_archive("my.tar.gz");

$tar = Archive::Tar->new; # 建立壓縮檔
$tar->read("origin.tar.gz", 1); # 讀取檔案
$tar->add_files("file/foo.c", "file/bar.c"); # 新增檔案
$tar->add_data("file/baz.c", "檔案內容"); # 新增內容
$tar->write("files.tar"); # 寫入壓縮檔

----------------------------------------------------------------

Encode
******

use encoding 'big5'; # 用 big5 碼寫程式
use open ':locale'; # 依地區設定決定輸出入編碼

$octets = encode(big5 => $string); # 從 Unicode 編碼為 Big5
$string = decode(big5 => $octets); # 從 Big5 解碼為 Unicode
from_to($octets, "big5", "utf-16"); # Big5 轉 UTF-16

# 指定使用 HTML 實體做為替代
$PerlIO::encoding::fallback = Encode::FB_HTMLCREF;
binmode(STDOUT, ':encoding(big5)'); # STDOUT 轉為 big5
print "游鍚�鶼鰤|長"; # 印出「游錫堃院長」

----------------------------------------------------------------

XML::Simple
***********

# 讀取 XML 成雜湊參照
my $ref = XMLin(q(


10.0.0.101

10.0.1.101



));
$ref->{server}{sahara}{address}[1] = '10.0.1.102'; # 更改元素
print XMLout($ref); # 印出 XML

----------------------------------------------------------------

SpreadSheet::ParseExcel
************************

# 另請參考 SpreadSheeet::WriteExcel
my $book = Spreadsheet::ParseExcel::Workbook->Parse('Test.xls');
foreach my $sheet (@{$book->{Worksheet}}) {
print "--------- SHEET: $sheet->{Name}\n";
foreach my $row ($sheet->{MinRow} .. $sheet->{MaxWor}) {
foreach my $col ($sheet->{MinCol} .. $sheet->{MaxCol}) {
$cell = $sheet->{Cells}[$row][$col] or next;
print "( $iR , $iC ) =>", $cell->Value, "\n";
}
}
}

Date::Manip

剖析常用日期格式 -- 日期物件的新標準請見 Datetime.pm

my @date = (
ParseDate("today"),
ParseDate("1st thursday in June 1992"),
ParseDate("05/10/93"),
ParseDate("12:30 Dec 12th 1880"),
ParseDate("8:00pm december tenth"),
);
# 比較與排序
my @sorted = sort { Date_Cmp($a, $b) } @date;
# 印出 "It is now 16:15:39 on Feb 7, 2003."
print UnixDate("today","It is now %T on %b %e, %Y.");
# 今天起的第三個工作天...
$date = &DateCalc("today","+ 3 business days",\$err);

Encode::HanConvert

好用的轉碼模組,可以繁,簡體轉換,而且還可以轉換相對應的詞庫,另外,還可以轉換為萬國碼。

$gbk = big5_to_gb($big5); # Big5 轉 GBK
$big5 = gb_to_big5($gbk); # GBK 轉 Big5
$simp = trad_to_simp($trad); # 萬國碼繁轉簡
$trad = simp_to_trad($simp); # 萬國碼簡轉繁

# 直接用內附的 b2g.pl 及 g2b.pl(-p 表示使用兩岸對照詞庫)
% b2g.pl -p < big5.txt > gbk.txt
% g2b.pl -p < gbk.txt > big5.txt

Lingua::ZH::Toke

可以針對中文字進行統計,只要建立起你想要統計的各種符號(包括段,詞,句,字等等),Perl就可以幫你算出各個符號的出現次數。

# 建立 Lingua::ZH::Toke::Sentence「句子」物件(->Sentence 亦可)
my $token = Lingua::ZH::Toke->new( '那人卻在/燈火闌珊處/益發意興闌珊' );
# 利用陣列解參照,輕易達成分段功能
print $token->[0] # 段 - 那人卻在
->[2] # 詞 - 卻在
->[0] # 字 - 卻
->[0] # 音 - ㄑㄩㄝˋ
->[2]; # 注音 - ㄝ
# 利用雜湊解參照,計算出現次數
print $token->{'那人卻在'}; # 1 - 出現一次片段
print $token->{'意興闌珊'}; # 1 - 出現一次單詞
print $token->{'發意興闌'}; # undef - 不是一個詞
print $token->{'珊'}; # 2 - 出現兩次字
print $token->{'ㄧˋ'}; # 2 - 出現兩次字音:益意
print $token->{'ㄨ'}; # 3 - 出現三次注音:那火處
# 逐項處理「段」物件
while (my $fragment = <$token>) {
# 逐項處理「詞」物件
while (my $phrase = <$token>) { ... }
}

Lingua::ZH::Summarize

可以處理中文的模組,主要是進行中文文章的摘要。

print summarize( $text ); # 印出文章摘要
print summarize( $text, maxlength => 500 ); # 500 個位元組的摘要
print summarize( $text, wrap => 75 ); # 在第 75 欄斷列

# 紅樓夢第一回:
細按則深有趣味。待在下將此來歷注明。必有補天濟世之材。那紅塵中有卻有些樂事。
究竟是到頭一夢。還只沒有實在的好處。使人一見便知是奇物方妙。因有個空空道人
訪道求仙。原來就是無材補天。據你自己說有些趣味。更有一種風月筆墨。雖不敢說
強似前代書中所有之人。哪�鶭峖酗u夫去看那理治之書!皆是稱功頌德。這東南一隅有
處曰姑蘇。最是紅塵中一二等富貴風流之地。這閶門外有個十�鶭庰鞳C街內有個仁清巷。
巷內有個古廟。倒是神仙一流人品。不辨是何地方。如今現有一段風流公案正該了結。
並不曾將兒女之真情發泄一二。如今雖已有一半落塵。不能洞悉明白。

----------------------------------------------------------------

OurNet::FuzzyIndex

可以針對一大堆的文件建立索引檔案,並且進行模糊搜尋。

my $db = OurNet::FuzzyIndex->new('test.db'); # 建立資料庫
$db->insert('資料甲', '這是一些中文字'); # 插入索引
$db->insert('資料乙', '這是另一些中文'); # 插入索引
$db->insert('資料丙', '這也是一些中文'); # 插入索引

# 以模糊搜尋進行全文檢索, 符合的存到 %result 中
my %result = $db->query('查詢中文字', $MATCH_FUZZY);

# 從上次找到的結果, 繼續搜尋其中「不符合」某字串的
%result = $db->query('也是', $MATCH_NOT, \%result);

# 用 $db->getkey 取得名稱, 印出結果
foreach my $idx (sort {$result{$b} <=> $result{$a}} keys(%result)) {
$val = $result{$idx};
print "找到: ".$db->getkey($idx)." (分數: $val)\n";
}

Text::Orientation

可以進行文字的旋轉,這是非常奇妙的功能,而且還可以處理中文字。

# 建立字串物件, 並指定編碼
my $o = Text::Orientation->new({
TEXT => "滿紙荒唐言\n一把辛酸淚\n都云作者痴\n誰解其中味\n",
CHARSET => 'utf-8',
});

# 右轉 90 度
print $o->rotate(1);

# 結果如下:
誰都一滿
解云把紙
其作辛荒
中者酸唐
味痴淚言


版權所有 (c) 2002, 2003 唐宗漢。本文是自由文件,可以依與 Perl 相同的授權條款散佈。