Perl 語法 - 基礎


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啟動時會默認打開三個文件句柄:STDOUTSTDINSTDERR,它們默認均與終端相連。

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}就可以了:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM