本文转自http://ind.ntou.edu.tw/~dada/cgi/Perlsynx.htm
前言:
這篇文章是花了我很多時間、費了我很多心血才完成的,雖然連我自己都覺得無法達到盡善盡美的境界,但希望能幫助大家入門,稍微了解到Perl到底是個什麼樣的東西,Perl到底有那些強大的功能,那麼這篇文章的目的就達到了。 我分做資料型態、控制敘述、副程式、I/O和檔案處理、Regular Expressions、Spectial Variables、Help 這幾部分來講解,但只是敘述了一些Perl的基本語法而已,Perl活潑的特性和程式的技巧就無法一一詳述了,甚為缺憾。
(1) 資料型態(Data type):
Perl的資料型態大致分為四種:Scalar、Scalar Array、Hash Array、References, 看起來雖少但用起來卻綽綽有餘。尤其在寫Perl程式時可以不必事先宣告變數,這一點對剛學程式語言的人甚為方便, 不過為了以後程式除錯和維護方便,我建議你還是養成事先宣告變數的習慣比較好。
純量變數是Perl裡最基本的一種資料型態,它可以代表一個字元、字串、整數、甚至浮點數,而Perl把它們都看成是一樣的東東! 你甚至可以混著用,不可思議吧。例如: # 井字號開頭的後面都是註解。
# 純量變數以$開頭。
# my 是一種宣告變數的方式,它可以使變數區域化。
# 宣告變數時若不加 my 或 local 則Perl會把它當作全域變數使用。
# 習慣上,我們會將字串用雙引號括起來,而數值就不用加引號。
my $x="abc";
my $x=123;
my $x=4.56;
那麼程式怎麼判斷這是數值還是字串呢? 其實不是程式判斷,而是你自己要判斷。Perl分別提供了一堆運算子來處理數字和字串,你必須知道這個變數是數值或字串,才能使用個別的運算子來對變數做運算。我分別列出字串運算子和數值運算子,好讓大家能區分它們的不同。
String Operator | Purpose |
x | Returns a string consisting of the string on the left of the operand, repeated the number of times of the right operand. |
. | Concatenates the two strings on both sides of the operator. |
eq | Returns True if the two operands are equivalent, False otherwise. |
ne | Returns True if the two operands are not equal, False otherwise. |
le | Returns True if the operand on the left is stringwise less than the operand on the right of the operator. Returns False otherwise. |
lt | Returns True if the operand on the left is stringwise less than or equal to the operand on the right of the operator. Returns False otherwise. |
ge | Returns True if the operand on the left is stringwise greater than or equal to the operand on the right of the operator. Returns False otherwise. |
gt | Returns True if the operand on the left is stringwise greater than the operand on the right of the operator. Returns False otherwise. |
cmp | Returns -1, 0, or 1 if the left operand is stringwise less than, equal to, or greater than the right operand. |
, | Evaluates the left operand, the evaluates the right operand. It returns the result of the right operand. |
++ | Increments the string by one alphabetic value. |
◎數值運算子
Value Operator | Purpose |
+ | Computes the additive value of the two operands. |
- | Computes the difference between the two operands. |
* | Computes the multiplication of the two operands. |
/ | Computes the division between the two operands. |
% | Computes the modulus(remainder) of the two operands. |
= = | Returns Ture if the two operands are equivalent, False otherwise. |
!= | Returns Ture if the two operands are not equal, False otherwise. |
<= | Returns Ture if the operand on the left is numerically less than or equal to the operand on the right of the operator. Returns False otherwise. |
=> | Returns Ture if the operand on the left is numerically greater than or equal to the operand on the right of the operator. Returns False otherwise. |
< | Returns Ture if the operand on the left is numerically less than the operand on the right of the operator. Returns False otherwise. |
> | Returns Ture if the operand on the left is numerically greater than the operand on the right of the operator. Returns False otherwise. |
< = > | Returns -1 if the left operand is less than the right, +1 if is it greater than, and 0(False) otherwise. |
&& | Performs a logical AND operation. If the left operand is True m then the right operator is not evaluated. |
|| | Performs a logical OR operation. If the left operand is True m then the right operator is not evaluated. |
& | Returns the valueof the two operators bitwise ANDed. |
| | Returns the valueof the two operators bitwise ORed. |
^ | Returns the valueof the two operators bitwise XORed. |
++ | Increment operator. Increments the variable's value by 1. |
-- | Decrement operator. Decrements the variable's value by 1. |
** | Computes the power of the left-hand value to the power of the rihght-hand value. |
+= | Adds the value of the right-hand operand to the value of the left-hand operand. |
-+ | Subtracts the value of the right-hand operand to the value of the left-hand operand. |
*= | Mlutiplies the value of the left-hand operand to the value of the right-hand operand. |
>> | Shifts the left operand right by the number of bits that is specified by the right operand. |
<< | Shifts the left operand left by the number of bits that is specified by the right operand. |
~ | Performs a 1s complement of the operator. This is a unary operator. |
純量陣列,陣列內的每一個元素都是Scalar variable。宣告及使用方式如下: # 純量陣列以 @ 開頭。
my @array;
my @array=qw(a b c d);
# qw 函數會將其後的每個元素用逗點隔開,效果就像下面這行。
my @array=("a","b","c","d");
# 當然你也可以一個個元素宣告,下面就是存取每一個元素的方法。
# 因為陣列中的每一個元素都是純量變數,所以要以 $ 開頭,
# 剛開始容易搞混,請注意。
$array[0]="a"; $array[1]="b"; $array[2]="c"; $array[3]="d";
# 使用for loop印出陣列內每個元素的值。
for($i=0; $i<=$#array; $i++) {
print "$array[$i]/n";
}
看到$#array這個奇怪的東東沒? 這是Perl的一個特殊用法,代表這個陣列最後一個元素的註標。 由於Perl不必事先宣告變數,也不必預先宣告陣列的大小,甚至可以隨時增加新元素,那我們怎麼知道這個陣列到底有多大呢? 透過這個特殊變數我們可以得知這個這個陣列最後一個元素的註標,自然而然也就知道這個陣列究竟有多大了。 另外Perl只定義了一維陣列的語法,二維以上只能用指標間接來達成。
雜湊陣列也叫做相關陣列,它和一般陣列沒什麼不同,差別只是在它的索引值用的是字串,而非一般陣列所用的整數值, 因此相關陣列不像一般陣列一樣有次序的概念,它沒有所謂的第一項資料這種說法。它就相當於把一堆變數組合成一個group,然後我們可以透過索引字串存取這個group每一個元素的值。 相關陣列的宣告及使用方式如下: # 相關陣列是以 % 符號開頭的。
my %hash;
# => 這個符號是Perl5新增的,是為了相關陣列量身定做的,
# 因為索引和元素值都是純量,若使用 => 這個符號,
# (索引=>元素值) 兩兩對應,就不容易發生失誤。
my %hash=("i1"=>"aaa","i2"=>"bbb","i3"=>"ccc");
# 上面這行的效果和下面這行是一樣的。
my %hash=("i1","aaa","i2","bbb","i3","ccc");
# 下面是存取每個元素的方法,注意是用大括號把索引括起來哦。
# 習慣上索引值用單引號、元素值用雙引號括起來。
$hash{'i1'}="aaa"; $hash{'i2'}="bbb"; $hash{'i3'}="ccc";
# 下面是使用相關陣列的三個例子:
foreach $key (keys %hash) {
print "$hash{$key}/n";
}
foreach $value (values %hash)
while(($key,$value)=each %hash)
Perl有上述三個函數可對相關陣列做運算:keys函數可取出相關變數的索引值,組成一純量陣列,注意這些由keys函數取出的索引值沒有次序性;values函數可取出相關變數的元素值;each函數則會取出(索引、元素)對。使用者可視情況而用。
Perl 5新增了參考指標的資料型態,使Perl和C一樣可借由指標建立一些複雜的資料結構。 普通程式是用不到指標這玩意的,下面也只是簡單介紹一下,看不懂的人可不必深究。
⊙如何取得變數的位址?
$scalarRef=/$scalarVar;
$arrayRef=/@arrayVar;
$hashRef=/%hashVar;
$funcRef=/&funcName;
⊙如何使用指標? print $$scalarRef;
print "@$arrayRef";
print $hashRef->{$key};
&$funcRef;
⊙Anonymous Array References:(二維陣列) $arrayRef=[[1,2,3,4],a,b,[x,y,z],c];
print "$arrayRef->[0][0]/t$arrayRef->[2]/t$arrayRef->[3][2]/n";
⊙Anonymous Hash References: $hashRef={a=>aa,b=>bb,c=>cc};
print "$hashRef->{a}/t$hashRef->{b}/t$hashRef->{c}/n";
(2) 控制敘述(Control Statements)
(a) Conditional Control Statements: Perl的條件控制敘述和C語言很像,讓使用者很快就能掌握它。不過Perl比C語言又另外多了些實用的語法,我用底線標出來,大家一看便知:
# Expression 就是條件敘述式,Perl和C一樣沒有定義布林資料型態(Boolean data type),
# 因此 0 是false、非0 是ture。另外要注意字串運算子和數值運算子要分清楚哦。
# Code Segment 就是用大括號括起來的一堆指令,也就是一個Block。
if (Expression) {Code Segment}
if (Expression) {Code Segment} else {Code Segment}
if (Expression) {Code Segment} elsif (Expression) {Code Segment} else {Code Segment}
# elsif 就是 else if
# 如果指令(statement)只有一項,我們可以使用倒裝句法,看起來比較簡潔。
statement if (Expression);
# unless 就是if not
statement unless (Expression);
例:
print "HELLO!/n" if ($name eq "friend");
$x-=10 if ($x == 100);
看吧! C 語言有的Perl大部分都有,學過 C 的人可以毫不費力的學會Perl。
# 注意:純量變數前面要加個 $ 字號,這一點和C語言不一樣哦。
Perl還有提供label(標記)的語法,也就是 goto 指令,不過有經驗的programer並不喜歡用它,我也不建議大家使用,所以就此按下不講。有興趣的人請自行查閱。 還有一點值得注意的是Perl沒有提供像C語言一樣的 switch 敘述,不過Perl的pattern match的功能非常強,所以我建議你直接用 if else 敘述來做就好了。
for($i=0; $i<=10; $i++) {Code Segment}
# foreach 是承襲UNIX的shell script來的,
# 第一個引數是純量變數,第二個引數要用括號括起來,裡面是一個純量陣列,
# 顧名思義它就是把陣列中的每個元素依序傳給第一個引數,直到全部傳完。
# 它和 for($i=0; $i<=$#array; $i++) 用法雖然不同,但目的都是要取出陣列的每個元素。
foreach $i (@array) {Code Segment}
# 其實在Perl中,for和foreach是可以混著用的,就看個的人習慣了。
# 下面這行就等於上面第一個敘述,不過簡潔多了,大家可以試著用用看。
for $i (0..10) {Code Segment}
# while控制迴圈和後置迴圈。
while($i<=10) {Code Segment}
do {Code Segment} while(Expression);
# Perl也有和C語言的break和continue一樣的指令,Perl叫它做 last 和 next (較口語化)。
# last是跳出現在所在的迴圈,next則是跳過下面的指令直接執行下一次的迴圈。
while(chomp($i=<STDIN>)) {
next if ($i == 5);
last unless ($i > 10);
}
(a) Syntax: sub NAME {Code}
(b) 呼叫副程式: &NAME(para1, para2,...)
(c) 參數傳遞: @_ Perl和C一樣是採用Call by value的方式,不過因為Perl不用事先宣告變數,所以建立副程式的時候也不用宣告要傳遞什麼參數。 當主程式在傳遞參數給副程式時,Perl會把括號括起來的參數按順序放在一個特殊的全域變數 @_ 陣列中,然後副程式就可以隨意使用陣列 @_ 裡的參數,例如 $_[0] 是第一個參數, $_[1] 是第二個,或是用
my ($a1,$a2,$a3,...) = @_;
來取出各個參數,當然 my @arg=@_;
或 my %arg=@_;
也是可以的。 由於Perl的語法非常活潑,使得程式在維護時特別棘手,因此寫註解成為一項很重要的工作。 我建議你最好在每個副程式前面加上對這段副程式的描述,特別是需要傳遞的參數要註明清楚。
my $x=3;
,若副程式所用的變數名不小心和主程相同,Perl會以目前正在執行的副程式裡的變數為優先。
(a) Syntax: open(FILEHANDLE,"Expression");
close(FILEHANDLE); 這裡的Expression是一個敘述加上檔案名稱,若Expression只有檔案名稱沒有加上敘述,則預設是唯讀。Expressions敘述如下:
Expression | Effect |
open(FH, "<filename") | Opens filename for reading. |
open(FH, "+<filename") | Opens filename for both reading and writing. |
open(FH, ">filename") | Opens filename for writing. |
open(FH, "+>filename") | Opens filename for both reading and writing. |
open(FH, ">>filename") | Appends to filename. |
open(FH, "command|") | Runs the command and pipes its output to the filehandle. |
open(FH, "command|") | Pipes the output along the filehandle to the command. |
open(FH, "-") | Opens STDIN. |
open(FH, ">-") | Opens STDOUT. |
open(FH, "<&=N") | Where N is a number, this performs the equivalent of C's fdopen for reading. |
open(FH, ">&=N") | Where N is a number, this performs the equivalent of C's fdopen for writing. |
例:
# 開啟$filename這個檔案,若開啟失敗則印出die後面的訊息,並結束程式。
open(FILE, $filename) || die "Can't open file $filename : $!/n";
# 下面是一個十分精簡的寫法,和 while($_=<FILE>){print "$_";} 是等效的。
print while(<FILE>);
# 檔案開啟後要記得隨手關閉,這才是寫程式的好習慣。
close(FILE);
# $!和$_都是Perl的特殊變數,下面會介紹的。
(b) Input: Perl沒有特別用來輸入的函數,因為Perl在執行程式時,會自動開啟標準輸入裝置,其filehandle定為STDIN,所以在Perl中要輸入資料的方法就是使用<STDIN>:
# Perl不會自動去掉結尾的CR/LF,跟C語言不同,所以要用chomp函數幫你去掉它。
# 大家常常會忘記這個動作,導致結果跟你想的不一樣,要特別注意一下。
$input=<STDIN>; chomp $input;
# 下面是較簡潔的寫法。
chomp($input=<STDIN>);
# 不用再指定變數的data type,這樣不是比printf()方便多了嗎?
print "Scalar value is $x/n";
# . 是字串加法的運算子,上下這兩行是等效的。
print "Scalar value is " . $x . "/n";
# 輸出到檔案的方法。
print FILE "print $x to a file.";
# 下面是print的特殊用法,學自shell script的用法:
print<<XXX;
這招叫做 here document,XXX可以是你取的任何識別字,XXX
在識別字之間的字都會按照你所寫的樣子輸出,就像<pre>標籤一樣。
而當一行的開頭是XXX你取的這個識別字時,才會停止輸出。
Perl 也有和 C 一樣以 "/" 開頭的特殊字元:
/t tab
/n newline
/r return
/f form feed
/b backspace
/a alarm(bell)
/e escape
/033 octalchar
/x1b hex char
/c[ control char
/l lowercase next char
/u uppercase next char
/L lowercase till /E
/U uppercase till /E
/E end case modification
/Q quoteregexp metacharacters till /E
另外需要說明的是 Perl 融合了 unix shell script 的使用慣例,以雙引號("")括起來的字串會先經過展開,但反斜線(/)後面的字元則不展開,當作一般字元看待。 而以單引號('')括起來的字串完全不會展開,以反單引號(``)括起來的字串會把它當作命令列指令一樣執行,等於system()一樣。 初學者常常會搞混,但習慣之後就會覺得不這樣分清楚反而不行哩。舉個例吧:
$x="ls -l";
print "$x"; # Output ls -l
print "/$x"; # Output $x
print '$x'; # Output $x
print `$x`; # Output files in this directory
(5) Regular Expressions
Regular Expression通常是用來尋找特定的字串樣式(pattern),也就是所謂格式辨認(pattern-matching)的功能。 它的運算子是『=~』和『!~』,可以把它念做match和not match。
Syntax: $string =~ /regular expression/expression modifier
例:$sentence =~ /Hello/
(a) Modifiers:修飾選項可有可無,它是用來對整個敘述作修正的。
g | Match globally, i.e. find all occurrences. |
i | Makes the search case-insensitive. |
m | If the string has new-line characters embedded within it, the metacharacters ^ and $ will not work correctly. This modifier tells Perl to treat this line as a multiple line. |
o | Only compile pattern once. |
s | The character . matches any character except a new line. This modifier treats this line as a single line, which allows . to match a new-line character. |
x | Allows white space in the expression. |
/ | Tells Perl to accept the following characters as a regular character; this removes special meanings from any metacharacter. |
^ | Matches the beginning of the string, unless /m is used. |
. | Matches any character except a new line character, unless /s is used. |
$ | Matches the end of the string, unless /m is used. |
| | Expresses alternation. This means the expressions will search for multiple patterns in the same string. |
( ) | Groups expressions to assist in alternation and back referencing. |
[ ] | Looks for a set of characters. |
* | Matchs 0 or more times. |
+ | Matchs 1 or more times. |
? | Matchs 0 or 1 times. |
{n} | Matches exactly n times. |
{n,} | Matches at least n times. |
{n,m} | Matches at least n times but no more than m times. |
/r | Carriage return(CR), ASCII 13(十進位) |
/n | New line, UNIX中代表ASCII 10(十進位), DOS(Windows)系統中則是ASCII 13 + ASCII 10(十進位). |
/t | Tab, ASCII 9(十進位) |
/w | Matches an alphanumeric character. Alphanumeric also includes _. 即 [A-Za-z0-9_]. |
/W | Matches a nonalphanumeric character. 即 [^A-Za-z0-9_]. |
/s | Matches a white space character. This includes space, tab, FormFeed and CR/LF. 即 [/ /t/f/r/n]. |
/S | Matches a non-whote space character. 即 [^/ /t/f/r/n]. |
/d | Matches a digit. 即 [0-9]. |
/D | Matches a nondigit character. 即 [^0-9]. |
/b | Matches a word boundary. |
/B | Matches a nonword boundary. |
/033 | octal char |
/x1B | hex char |
/abc/
/^abc/
找到開頭是abc的字串
/abc$/
找到結尾是abc的字串
/a|b/
找到有a或b的字串,也可以用來找整個字(word)
/ab{2,4}c/
找到a後面跟著2-4個b,再跟著c的字串,若只有/ab{2,}c/則會找二個以上的b
/ab*c/
找到a後面跟著0個或多個b,再跟著c的字串,如同/ab{0,}c/
/ab+c/
找到a後面跟著一個以上的b,再跟著c的字串,如同/ab{1,}c/
/a.c/
.可以代表任何字元,除了new line字元(/n)外。
/[abc]/
找到含有這三個字元中任何一個的字串
//d/
找到含有數字的字串,如同/[0-9]/
//w/
找到含有字母的字串,如同/[a-zA-Z0-9_]/
//s/
找到含有white space的字串,如同/[ /t/r/n/f]/
/[^abc]/
找到沒有abc任一字元的字串
//*/
找到含有字元*的字串,在反斜線"/"後面的字元Perl會把它當作普通字元看待。若你不確定這個符號是否為特殊字元,乾脆全加上/以策安全。
/abc/i
忽略abc的大小寫
/(/d+)/.(/d+)/.(/d+)/.(/d+)/
找到類似IP的字串,並將IP的四個數字分別存在$1,$2,$3,$4四個特殊變數中,以便在其後加以利用。例:
if ($x =~ /(/d+/./d+)/./d+/./d+/) {
print "海洋大學" if ($1 eq "140.121");
}
m//gimosx
m命令可以讓你自訂pattern的分隔符號,而gimosx則是它的修飾選項,請參看(a)Modifiers。例如:
$url="my.machine.tw:8080/cgi-bin/test.pl";
($host, $port, $file)=($url=~m|http://([^/:]+):{0,1}(/d*)(/S*)$|);
這個Regular Expression相當複雜,主要目的是分析指定的URL,然後取得host名稱、port號碼及對應的檔案。我一項項慢慢解釋:
$url=~m||
m後面跟著的就是分隔符號,| |裡面的就是pattern。
([^/:]+)
match一個字串,裡面沒有/和:字元。找到的字串存在$1中。
:{0,1}(/d*)
match 0或1個:,後面跟著一串數字或nothing。找到的字串存在$2中,若找不到,$2就是空的。
(/S*)$
match一串非空白字元,並以找到的字串為結尾。找到的字串存在$3中。
()=()
($host, $port, $file)=($1, $2, $3)
即$host="my.machine.tw"
$port=8080
$file="/cgi-bin/test.pl"
s/PATTERN/REPLACEMENT/egimox
沒錯,這就是取代的命令。它會尋找符合PATTERN的字串,並取代成REPLACEMENT字串。它的修飾選項多了e選項,其他的和上面都一樣,我將它列表如下:
e | Evaluate the right side as an expression. |
g | Replace globally, i.e. all occurrences. |
i | Do case-insensitive pattern matching. |
m | Treat string as multiple lines. |
o | Only compile pattern once. |
s | Treat string as single line. |
x | Use extended regular expressions. |
例:$x =~ s//s+//g
把所有的white space全部去除掉
$x =~ s/([^ ]*):*([^ ]*)/$2:$1/
把用":"分開的兩個欄位互相對調
$path =~ s|/usr/bin|/usr/local/bin|
它也可以讓你自訂分隔符號哦
tr/SEARCHLIST/REPLACEMENTLIST/cds
c | Complement the SEARCHLIST. |
d | Delete found but unreplaced characters. |
s | Squash duplicate replaced characters. |
例:$x =~ tr/this/that/
把"this"替換成"that"
$x =~ tr/a-z/A-Z/
把小寫字母全部替換成大寫字母
$count = $x =~ tr/*/*/
計算$x中有幾個"*"
Perl的特色之一就是有超過50個以上的特殊變數,這些變數都是全域變數,用來設定程式的執行環境和其它細節。 若你想深入了解Perl程式設計,那麼這些東西是不可或缺的。在這裡我只列幾個常用的特殊變數以供參考,有興趣的人請自行查閱。
$_ | The default input and pattern-searching space. |
$digit | Contains the subpattern from a successful parentheses pattern match. |
$. | The current input line number of last filehandle read. |
$! | Contains the current value of errno. |
$0 | The name of the file of the Perl script. |
@ARGV | The command line arguments issued when the script was started. |
@_ | The parameter array for subroutines. |
%ENV | This associative array contains your current environment. |
首先你可以到各大BBS站的WWW板或program板的精華區找找,不過資料不會很多,而且有點雜亂。 再不然你可以到蕃薯藤輸入perl或cgi關鍵字找找,有不少人的homepage上有教人如何用Perl寫CGI程式,還有一堆別人已經寫好的CGI程式可以抓來用用。 說到這裡順便打一下廣告,在http://ind.ntou.edu.tw/documents下有Perl的說明文件,做得相當不錯,不過全是英文的就是了,而我這份講義打好後也會放在IND的homepage上,大家可以抓回去參考參考。
第三種資源就是Perl本身的man pages,你在IND帳號下鍵入 man perl 就可以查閱了,這份文件做得不錯,還根據Perl的各個部分寫了man pages,如Perl syntax, builtin function, regular expression, data structure等, 大家可以用man指令一一查詢,有時候在寫程式時一時忘了某個函數怎麼用,這倒是個方便又快速的查詢方法。我會把這些man pages轉成html格式,放在WWW上讓大家參考。