perl語言的核心是正則表達式,在文本處理上非常有優勢,與python類似,但語法不同,perl的語法很靈活,用多了才會覺得好用。
常用知識點總結:
- perl語法類似於C語言(perl源於Unix),語句由逗號划分,代碼層次使用花括號{}划分,但是不必聲明變量類型;
- 標量變量($name),數組(@name),哈希結構(%name),類型標識符,文件句柄沒有標識符;
- 哈希結構可以使用列表創建,但不要以為它也是由圓括號括起來的;在使用鍵時,用花括號。(特別注意)
- 數字之間比較用(==、>=、<=、!=),字符串之間比較則用(eq、gt、lt、ge、le)
- print函數,不一定需要括號。幾種情況:print \$name(直接輸出) ;print ‘\$name’(基本不用,錯誤的,原樣輸出); print “\$name”(有時會用,會自動替換); print 函數在做文件輸入時(文件句柄),不能有逗號,只能用空格。
- @_ 是函數傳參時放置參數的數組,可以從中取實參;\$_ 是默認參數的意思,指的是在不指定的情況下,程序處理的上一個變量;shift 是將數組的第一個元素 \$array[0] 移走, 並將這個元素回傳(return) (堆棧一節有詳細講解)。
- shift函數是取數組的第一個元素,缺省就取@_的第一個函數,這句一般用在程序的開頭,用於接收程序的參數,或者子函數的開頭,用於接收子函數的參數。
- 句柄和文件的關系,文件必須被打開,並賦與句柄,才能操作;有的句柄可以直接使用,如STDIN、STDERR;廣義上,標量變量就是一種代表數據的句柄。
- perl中數值和字符串可以隨意的使用遞增和遞減運算符。
第1學時 P e r l 入門
- 安裝perl
- perl內部文檔
- 第一個perl腳本
通用編程語言,膠水語言,perl擅長將其他程序連接到一起。
# 檢查perl是否安裝成功 perl -v
ActiveState Tool公司提供了一個自行安裝Perl的工具(安裝十分簡單)。
# 在Linux下安裝perl # 解壓 tar -zxvf stable.tar.gz -C dir $ gunzip stable.tar.gz $ tar xf stable.tar # 配置 $ sh Configure # 安裝 $ make $ make test #測試是否make成功 # make install
安裝文件有完整的perl幫助文檔
perldoc perl
perl手冊分如下三個部分:
- perlfunc
- perlop
- perlfaq
perl perlfunc
# 查找某個函數名 perldoc -tf print # 搜索FAQ文件 perldoc -q support
各人不推薦在命令行窗口查閱文檔,閱讀模式太不友好,可以閱讀HTML格式文檔。
可以在瀏覽器中打開本地HTML文檔:file:///D:/Program%20Files/Perl64/html/index.html
#第一個perl程序 #!/usr/bin/perl -w print "Hello, World!\n";
#運行perl腳本 perl test.pl
linux下可以以可執行文件形式運行perl腳本,但首先必須修改權限,使其可執行。
chmod 755 test.pl ./test.pl
第2學時 P e r l 的基本構件:數字和字符串
- 標量常量:數字 和 字符串
- 標量變量:存放標量,
字符串
只有使用轉義字符才能在字符串中插入特殊的字符。
- 單引號:‘ ’中的每個字符都表示它本身,$a, \n等等,都不會進行處理,原樣輸出。
- 雙引號:“ ”中Perl會查看是否存在 變量名 和 轉義序列,一旦發現,會自動替換。
如何便捷的將字符串用 雙引號 或 單引號 括起來?
- qq運算符:qq(I said, “I love you!”) 用法如上
- q運算符: q(Tom’s tree) 用法與之前單引號一樣,會原樣輸出單引號內的所有內容。
標量變量
特殊變量($_):當前默認值,
運算符(運算符之間可以有空格)
數字運算符有哪些?
加+;減-;乘*;除/;求余%;取冪**;
字符串運算符有哪些?
- 並置運算符(.)(運算對象必須為字符串標量或者標量變量);
- 可以使用反斜杠\,指定屏蔽字符串內的內插替換(標量變量和轉義字符);
- 如果字符串中標量名和字符串過於緊密,可以用{}顯示指定變量名,如${name}space;
- 重復運算符(x),如“-”x 10;
帶名字運算符?
int(5.20); length(“nose”); lc(“ME TOO”); uc(“hal 9000”); cos(50); rand(5)(返回隨機浮點數,默認是0-1)
尖括號運算符?
用於讀寫文件,<STDIN>,從標准輸入讀入一行;默認會讀入我們最后輸入的回車,可以使用從chomp運算符去掉;
chomp可以去除任何參數結尾的換行符,返回被刪除的字符數(刪除了返回1,沒刪除返回0)。
第3學時 控制程序流 (foreach是核心)
- 語句塊
- 運算符
- 循環
- 標號
- 退出perl
if語句(幾乎與C語言類似,除了elsif)
$r = 10; if ($r == 10){ print '$r is 10!'; } elsif ($r == 20){ print '$r is 20!'; } else{ print '$r is neither 10 nor 20.' }
# 如果只有一個條件可以把if放到后面 print "Hello!" if (1 == 1);
字符關系運算符(類似於=, >, <, >=, <=, !=)?
- eq等於;
- gt大於;
- lt小於;
- ge大於等於;
- le小於等於;
- ne不等於;
perl中有哪些假值? 0;“”;‘’;‘0’;“0”;undef;
邏輯運算符有哪些? and; or; not;
if ($x and $y and not $z){ print "All conditions met.\n"; }else{ print "Not all.\n" }
邏輯運算符短路,即一旦確定表達式是真或假,就立即停止邏輯表達式的計算,最常見的是
open(FILE, "test.txt") or die "can't open file:$!\n";
循環語句(與C語言完全相同)
$count = 0; while($count <= 10){ print "$count\n"; $count++; }
for($i; $i <= 10; $i++){ print "i is now $i\n"; }
# 高級perl技巧:使用foreach進行遍歷 @name = qw(li zhi xin); print "my name is "; foreach $name (@name){ # $name是對@name中元素的引用,可以修改@name print "$name " }
流程控制工具
last語句(退出當前循環)(等同於C里的break)
$count = 1; while($count <= 15){ last if ($count == 5); print "$count\n"; $count++; }
for($i=0; $i <= 100; $i++){ for($j=0; $j <= 100; $j++){ if ($i * $j == 140){ print "The product of $i and $j is 140.\n"; last; } } }
next語句(回到當前循環頭部)(等同於C里的continue)
for($i=0; $i <= 100; $i++){ next if (not $i % 2); print "An odd number = $i\n"; }
語句塊和循環語句可以設置標號,last、redo 和 next都可以帶一個標號。(等價於C的loop語句)
退出perl? exit 0;代表正常退出perl。
第4學時 基本構件的堆棧:列表與數組
- 填充和清空數組
- 逐個元素查看數組
- 對數組進行排序和輸出
- 標量分割為數組
- 數組合並為標量
列表是常量,數組是變量,可以將列表存放在數組當中。如:
(12,’abc‘,3.14,True) # 這是一個直接量列表
如果列表中全部是字符串,為了避免注意添加單引號,可以使用qw運算符,如
# 兩者等價,最好不要有內嵌變量 @a = qw(abc def ghi) @b = ('abc','def','ghi')
@c = ()
列表的范圍運算符(..)
@a = (1..10) @b = (1..10, 21..30) # 字符照樣可以使用范圍運算符 @list=(aa..zz); foreach (@list){ print "$_ "; }
為什么要把列表這個東西單獨拿出來?
因為列表是有特殊作用的常量,可以對其他結構進行初始化(數組和哈希)。
數組(列表)之內的數組會自動合並成一個大的數組。
可以利用數組的特性進行批量賦值
($a, $b, $c) = qw(li zhi xin);
如何從數組中獲取元素?
@name=qw(li zhi xin); print @name; #沒有空格 print "\n@name\n"; #元素之間有空格 print scalar(@name); #輸出數組元素個數
@a = qw(li zhi xin); print @a; # 每個數組元素之間緊密排列,沒有空格和換行
@a = qw(li zhi xin); print $a[0]; print $a[1]; print "\n"; $a[2] = "wei"; print @a;
如何將數組划分成片?
@a = qw(li zhi xin 1 2 3 4 5 6); @b = @a[2,4,6]; print @b;
如何尋找數組的結尾?
@a = qw(li zhi xin 1 2 3 4 5 6); print $#a; # 返回結尾元素的索引
@a = qw(li zhi xin 1 2 3 4 5 6); $size = @a; print $size; #根據上下文,返回數組元素的個數
print $a[-1]; # 返回數組的最后一個元素
如何測試數組中是否包含元素?
@mydata = qw(li zhi xin); if (@mydata){ print "Not empty!\n"; }
上下文
- 標量上下文
- 列表上下文
強制使參數成為上下文?
print scalar(@mydata); # scalar( )函數
$a = <STDIN>; # 讀入一行 @whole = <STDIN>; # 讀入整個 ($a) = <STDIN>; # 全部讀取,但只保存一行到$a
@stars = ('*') x 10; foreach $star (@stars){ print "$star\n"; }
print scalar(localtime);
chomp:標量下,去除標量結尾記錄分隔符;列表下,去除每個標量結尾記錄分隔符。<STDIN>同理。
對數組進行操作
如何遍歷?
# 普通方法:使用for循環進行遍歷 @name = qw(li zhi xin); print "my name is "; for ($index = 0; $index < @name; $index++){ print "$name[$index] "; }
# 高級perl技巧:使用foreach進行遍歷 @name = qw(li zhi xin); print "my name is "; foreach $name (@name){ # $name是對@name中元素的引用,可以修改@name print "$name " }
foreach遍歷時,標量為列表元素的引用,可以同時修改數組(列表)中的元素。
數組和標量之間進行轉換?
split函數,擁有一個模式和一個標量,可以使用模式來分割標量。
join函數,去除一個字符串和一個數組(列表),使用該字符串將數組中的元素連接起來。
@word = split(/ /, "The quick brown fox"); foreach $word (@word){ print "$word\n" }
$numbers=join(',', (1..10)); print $numbers;
數組排序?
排序后,原始的數組不變(python比較智能,有兩種排序函數)。
@chiefs = qw(li zhi xin); print join(' ', sort @chiefs);
指定方法排序(硬方法 和 飛船運算符):
@numbers=(1,5,2,7,3,8,4); @sorted=sort {return(1) if($a>$b); return(0) if($a==$b); return(-1) if($a<$b); } @numbers; print @sorted;
@numbers=(1,5,2,7,3,8,4); @sorted=sort {$a <=> $b} @numbers; #a,b的順序決定排序的類型 print @sorted;
字符串排序,需要使用cmp運算符,編寫一個復雜的排序代碼。
倒序操作?
分標量上下文(對標量里每個字符倒序) 和 列表上下文(對元素整體倒序)。
@lines = qw(li zhi xin); print join(' ', reverse @lines);
第5學時 進行文件操作
perl的標量可以延長,存放足夠長的一行內容;perl的數組可以擴展,可以存放文件的全部內容。
不在將輸入和輸出局限於鍵盤和終端,可以將輸入和輸出定向到指定文件。
- 打開和關閉文件
- 將數據寫入文件
- 從文件讀取數據
- 健壯性
打開文件
文件句柄是對特定文件的一個引用(也是一種變量),包含了如何打開文件(文件位置)、位置信息(讀到哪了)和讀寫屬性(只讀、覆蓋、追加)。STDIN通常與鍵盤相連,從鍵盤輸入信息。最好使用大寫字母。
如果想打開文件,必須先創建一個句柄:open(filename, pathname),若打開失敗,就會返回undef。
# 低級的打開和撤銷策略 if (open(MYFILE, "mydatafile")){ # 成功運行 }else{ print "Cannot open mydatafile!\n"; exit 1; }
die函數可以停止函數的運行,利用了條件語句的執行特性
# 升級版打開或撤銷 open(MYTEXT, "novel.txt") || die; close(MYTEXT);
默認是在當前文件夾查找文件,可以指定路徑名。
# die函數升級版:$!系統需要的最后一個操作的出錯消 open(MYFILE, "myfile") or die "Cannot open myfile: $!\n";
# 用warn取代die,程序不停止,只發出警告。 if (!open(MYFILE, "output")){ warn "Cannot read output: $!\n"; }else{ # 執行代碼 }
讀取文件
讀取一行文本,完全類似於<STDIN>,默認該句柄已經打開。
# 讀取一行文本 文件讀完了<MYFILE>返回 undef
open(MYFILE, "myfile") or die "Can't open myfile: $!\n"; $line = <MYFILE>;
讀取和輸出整個文件
while(defined($a=<MYFLIE>)){ #defind()函數,檢查表達式的值是否是undef print $a; }
# 高級寫法 while(<MYFILE>){ print $_; }
# 使用列表讀取存儲整個文件,只有在文件非常小時才這么做,非常耗內存。 open(MYFILE, "novel.txt") or die "$!"; @content = <MYFILE>; close(MYFILE);
以下是語法在只讀模式打開file.txt的。這里小於< signe 指示,文件必須以只讀模式運行結束:
open(DATA, "<file.txt"); while(<DATA>){ print "$_"; }
寫入文件
想要寫入文件,在打開文件句柄時就必須指定文件的打開方式(>, >>)。
# 寫入並覆蓋 open(NEWTH, ">output.txt") or die "Opening output.txt: $!"; # 寫入並追加 open(APPFH, ">>logfile.txt") or "Opening logfile.txt: $!";
也可以用print( )將數據寫入文件句柄
open(LOGF, ">>logfile") or die "$!"; if (! print LOGF "Time", scalar(localtime), "\n"){ warn "Unable to write to the log file: $!"; } close(LOGF);
自由文件句柄
perl啟動時會默認打開三個文件句柄:STDOUT、STDIN 和 STDERR,它們默認均與終端相連。
print函數默認是使用STDOUT
如何寫入二進制的數據?
open(FH, "camel.gif") or die "$!"; binmode(FH); # 標記為二進制文件 print FH "GIF87a\056\001\045\015\000"; close(FH);
文件測試(檢查文件是否存在,權限)
-e #文件存在,則真 -d #是個目錄,則真 -r #文件可讀,則真 -T #文本文件,則真 -B #二進制文件,則真
if (-s $filename){ warn "$file contents will be overwritten!\n"; warn "$file was last updated", -M $filename, "days ago.\n"; }
第6學時 模式匹配(perl的核心)
識別輸入數據流的模式(模式==正則表達式),需要對數據進行格式化,簡單的split函數無法完成。
- 創建簡單的正則表達式
- 正則表達式進行模式匹配
- 正則表達式編輯字符串
什么是模式?
模式被括在模式匹配運算符中間,基本形式:m//;如:m/simon/。($_常常作為匹配的默認值)
print "yes\n" if(m/Piglet/); # 如果使用的是斜杠//,那么m可以省略。
- 除非是元字符,否則會嚴格匹配(可以用\屏蔽元字符)
- 可以使用任意匹配的字符來替代斜杠(如m::)
- 如果是斜杠,則可以省略前面的m
- 變量也可以放在匹配模式里
元字符
# 圓點(.):匹配除了換行符外的任何單個字符,必須存在一個字符,但不能多於一個,僅且只能。 /p.t/ #可以匹配pot、pat,不能pt,不能paat;
#以下都是通配符(並非一對一) # (+):使前面的字符與后面的字符最少匹配一次 /do+g/ #可以匹配dog、dooooog,不能dg; # (*):進行0次或多次匹配,與(+)非常類似,但可以0次(注意:這和shell下的通配符意義完全不同) /car*t/ #可以匹配cart、cat、carrrt; # (?):前面的字符進行0次或一次匹配(不能超過一次),限定死了,0次或1次,只有兩種可能 /c?ola/ #可以匹配cola、ola
# pat{n, m} # n:匹配的最小次數;m:匹配的最大次數
/x{5, 10}/ #x最少匹配5次,最多匹配10次
# (.*):可以匹配任何東西
字符類(特征[],必須有方括號加以限定)
[abcde] #匹配其中任何一個字符 [a-e] #同上 G #匹配G或者g [0-9] #匹配一個數字
[0-9]+ #順序匹配一個或多個數字 [A-Za-z]{5} #匹配任何一組5個字母字符
$%&()] #匹配這些符號中任何一個
/[^A-Z]/ #不匹配A-Z中的任何單個字符
字符類快捷方式
\w #一個單詞字符,同[a-zA-Z0-9_] \W #與\w相反 \d #一個數字,與[0-9]相同 \D #與\d相反 \s #一個白空間字符,同[\t\f\r\n] \S #與\s相反
分組和選擇(這個開始有點復雜)
if(/dogs|cats/){ print "\$_ contains a pet\n"; }
/(fr|b|fl|cl)og/;
在列表上下文中,匹配運算符返回匹配表達式各部分的列表,每個加括號()的都是其中一個返回值,其中$1是第一個匹配的括號。
$_="apple is red"; ($fruit, $color) = /(.*)\sis\s(.*)/; print $fruit." "; print $color;
/^Help/ #只匹配Help開頭的行 /^one.*two$/ #只匹配one開頭和two結尾的行 /^only$/ #只匹配包含only的行 /^$/ #匹配空行 /^/ #匹配非空行
替換
替換運算符(s///):s/查找模式/替換模式/;
$_="Our house is in the middle of our street."; s/middle/end/; s/in/at/; {do something} if (s/apartment/condo/);
對其他變量進行模式匹配操作(對非$_變量使用匹配運算符,必須用=~將變量與模式連接起來)
$a="apple is red"; ($fruit, $color) = $a=~/(.*)\sis\s(.*)/;
$_="one fish, two frog, red fred, blue foul"; @F=m/\W(f\w\w\w)/g;
/macbeth/i #不考慮大小寫
反向引用(將括號用於正則表達式)
括號內的匹配模式被記錄在特殊的變量當中,分別為$1, $2, $3, $4.
$number="800-555-1212"; if ($number =~ /(\d{3})-(\d{3})-(\d{4})/){ print "the area code is $1"; }
模式匹配運行成功時,變量$1, $2, $3才被設置,之后會被清除。(還是不懂什么時候可以用???)
強大的grep函數(以任意的條件搜索數組)
#grep迭代運行通過list中的每個元素,$_設置為列表的每個元素,然后依次執行表達式或模式匹配,表達式為真,則返回該元素,賦值給數組。 @dogs=qw(greyhound bllodhound terrier mutt chihuahua); @hound=grep /hound/, @dogs; print "@hound";
#$_為實際值的引用,可以修改原始元素 @dogs=qw(greyhound bllodhound terrier mutt chihuahua); @hound=grep s/hound/hounds/, @dogs; print "@hound";
#grep可以使用任何表達式 @dogs=qw(greyhound bllodhound terrier mutt chihuahua); @hound=grep length($_)>8, @dogs; print "@hound";
map函數
第7學時 哈希結構(%)
- 創建哈希結構
- 哈希中插入和刪除元素
- 用哈希對數組進行操作
哈希結構不用定義,可以直接使用,對哈希中單個元素進行賦值就可以創建哈希結構,如下:
$record{'name'} = "lizhixin"; #原汁原味的哈希 print $record{name}; #引號可以省略,可以識別其為字符串,因為變量有$
整個哈希結構可以使用(% info)來操作,比如創建和賦值:
# 就這么兩行,想一次性寫對還是挺難的
%info=('name', 'lizhixin', 'age', '24'); #可以以=>的形式創建 print $info{'name'}; # 引號可以省略
注意:我們只能用列表來創建哈希,因此第一行只能用圓括號(),而不能使用花括號{ },這和python中的用法不同。
而是想訪問哈希中的某個元素的時候,只能以($)開頭,表示單個哈希元素,哈希的鍵值必須用花括號{}包裹,
本例中{}里面的鍵必須用單引號包裹,否則name可能是個變量,解釋器無法識別
。
如何遍歷哈希結構中的鍵 和 值?(keys函數 和 values函數),低級遍歷
foreach $a (values %dict) # or keys %dict{ print "$a\n" }
如何按值來檢索哈希中的元素?反轉,此例為高級遍歷。
%hash = ("li"=>"1", "zhi"=>"2", "xin"=>"3"); %re_hash = reverse %hash; foreach (keys %re_hash){ print "$_ => $re_hash{$_}\n" }
如何在列表(數組)和哈希之間轉換? 很簡單,直接互相賦值
%hash = ("li"=>"1", "zhi"=>"2", "xin"=>"3"); @hash = %hash; foreach (@hash){ print "$_\n"; }
如何測試哈希結構中是否存在某個關鍵字? 可以自己寫個函數,也可以直接用現成的exist()函數
# 自己寫的exist函數 %hash = ("li"=>"1", "zhi"=>"2", "xin"=>"3"); $hash = "liz"; $flag = 0; foreach (keys %hash){ $flag = 1 if($_ eq $hash); } if ($flag == 1){ print "$hash exist"; }elsif ($flag == 0){ print "$hash not exsit"; }
# 直接調用現成的exists函數 if (exists $hash{$hash}){ print "$hash exist"; }else{ print "$hash not exsit"; }
如何刪除哈希關鍵字?
delete $hash{li};
哈希結構排序
foreach (sort keys %Words){ print "$_ $Words{$_}\n"; }
第8學時 函數
- 如何定義函數和調用函數
- 值傳遞 和 返回值
- use strict
# 創建一個Hello World子例程 sub HelloWorld{ print "Hello World!\n"; }
# 調用子例程的兩種方法 &HelloWorld(); HelloWorld();
函數能返回所有的數據類型
# 此例返回數組 sub shift_to_uppercase{ @words=qw(li zhi xin); foreach (@words){ $_ = uc($_); } return(@words); }
函數參數(有點復雜)
被傳遞的參數可以通過特殊變量@_來訪問。
sub printargs{ print join(',', @_); } printargs('market', 'home', 'roast beef');
如何訪問函數的具體的某個參數?($_[i])
sub print_third_argument{ print $_[2]; }
作用域
默認的變量是全局變量。
為了保證函數的封裝性,函數里的變量必須為專用變量,可以使用my操作符(只在當層模塊可見)。
local變量(局部變量)(在當層,以及以內的模塊都可用)
use strict
參考:
Perl程序實例
join可以將數組(列表)合並為標量(為啥qq生成的數組不行,qw可以)
@a = ('li', 'zhi', 'xin'); $b = join("-", @a);
如何迭代數組和哈希?
@a = ("li", "zhi", "xin"); foreach (@a){ print "$_\n"; } %b = ("a" => "abc", "d" => "def", "e" => "efg"); foreach (keys %b){ print "$_ => $b{$_}\n"; }
如何傳遞參數?
perl很奇怪,定義函數時沒有標志性的圓括號 和 參數列表,以至於初學者不知道如何傳遞參數。
其實,參數被傳遞給了perl的特殊變量 @_ (它是一個列表) (類似於$_):
sub printargs { print join(',', @_); } printargs("li", "zhi", "xin");
如何單獨訪問指定的參數? 使用@_ 數組的下表進行准確操作,但是換了符號,得用 $_[0], $_[1]…
sub print_third_argument{ print $_[2]; } print_third_argument("li","zhi","xin");
unless 是 if 的反義詞
-e 等參數用於檢查 文件和目錄是否存在,以及其他屬性。
<=>飛船運算符如何使用?
@number=qw/5 10 15 3 2 4 8 6 /; my @descending =sort {$a<=>$b} @number;#這里$a=5,$b=10,因為$a在前,perl得到的結果是小數在前,也就是升序排列 print "@descending\n"; # 結果:2 3 4 5 6 8 10 15 # 如果要降序排列,只要把程序的第二行變成sort{$b<=>$a}就可以了:
