-
重新理解正則表達式甥温,Perl中的正則表達式其實不是獨立的功能,只是內(nèi)嵌于某些功能中的子語言,
m//
就像是文本編輯器中的搜索功能济丘,Perl同樣提供了替換功能:s/正則表達式的模式/替換的內(nèi)容/
惭载,如果沒有用=~
綁定對象敢订,則默認替換的是預(yù)置變量$_
demo9-1
:
#!/usr/bin/perl
$_="This is a test.\n";
if($result = s/is/was/){
print $_;
}
print "result:$result";
作為表達式,s///
返回匹配結(jié)果的布爾值:
./demo9-1
Thwas is a test.
result:1
-
s///
默認只會替換第一個匹配的位置厅缺,如果要實現(xiàn)全部替換蔬顾,需要修飾符/g
demo9-2
:
#!/usr/bin/perl
$_="This is a test.\n";
s/is/was/g;
print $_;
./demo9-2
Thwas was a test.
m//
中的修飾符在s///
中同樣可以使用宴偿,包括:
/i
:忽略大小寫
/s
:讓.
匹配包括換行符在內(nèi)的任意字符
/x
:允許在模式中插入任意空白和換行
- 和
m//
一樣,在s///
中也可以使用捕獲變量
demo9-3
:
#!/usr/bin/perl
$_="This is a test.\n";
s/(\w+) (\w+) (\w+) (\w+)/$4 $3 $2 $1/;
print $_;
把捕獲的變量逆序輸出
./demo9-3
test a is This.
我們也可以把反向引用加進去:
demo9-4
:
#!/usr/bin/perl
$_="This is a test.\n";
s/(is) \1/were/;
print $_;
匹配到is is
./demo9-4
Thwere a test.
命名捕獲變量和自動捕獲變量同樣可以使用:
demo9-5
:
#!/usr/bin/perl
$_="This is a test.\n";
s/(?<be>is)/_$+{be}_/;
print $`."\n";
print $&."\n";
print $'."\n";
print $_;
./demo9-5
Th
is
is a test.
Th_is_ is a test.
-
s///
的定界符可以替換诀豁,如果是非成對定界符(比如#
)只需要重復(fù)三次來分割圈引模式和圈引替換模式窄刘,但如果是成對定界符(比如{}[]
)就需要成對使用兩次來區(qū)分圈引模式和圈引替換模式:
s#abc#def#
s{abc}[def]
(雖然#
也被用來表示注釋, 但如果Perl解析器在期待一個定界符,就不會把#
視作注釋的開頭)
- 在
s///
的圈引替換模式中舷胜,有一組特殊的轉(zhuǎn)義字符可以實現(xiàn)大小寫轉(zhuǎn)換:
\U
:之后的字符全部轉(zhuǎn)換成大寫
\L
:之后的字符全部轉(zhuǎn)換為小寫
\E
:結(jié)束\U
和\L
的作用范圍
\u
:之后的第一個字符大寫
\l
:之后的第一個字符小寫
可以將\u\L
或者\l\U
連用實現(xiàn)首字母大寫或首字母小寫的功能:
demo9-6
:
#!/usr/bin/perl
$_="This is a test.\n";
print $_;
s/(?<be>test)/\u\L$+{be}/;
print $_;
./demo9-6
This is a test.
This is a Test.
大小寫轉(zhuǎn)義字符的規(guī)則并不是s///
專屬的娩践,任何雙引號內(nèi)的字符串都適用此規(guī)則
demo9-7
:
#!/usr/bin/perl
$_="This is a \u\Ltest.\n";
print $_;
s/(?<be>test)/\l\Utest/i;
print $_;
./demo9-7
This is a Test.
This is a tEST.
- 另一個使用正則表達式的是
@result = split //,$string
,操作符split
會根據(jù)分隔符(寫成模式)將一個字符串截斷成子字符串列表。
demo9-8
:
#!/usr/bin/perl
$input = "Zhao:Qian:Sun:LI:Zhou:Wu:Zheng:Wang";
foreach (split /:/, $input){
print $_."\n";
}
分隔符自己不會出現(xiàn)在列表中
./demo9-8
Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang
- 如果字符串中兩個分隔符連著就會產(chǎn)生空字段烹骨,但是Perl會忽略末尾的空字段:
demo9-9
:
#!/usr/bin/perl
$input = "::Zhao::Qian::Sun::LI::Zhou::Wu::Zheng::Wang::";
foreach (split /:/, $input){
print $_."\n";
}
在原來的每個字段之間和前后都加了額外的分隔符翻伺,但是結(jié)尾的空字段被舍棄了
./demo9-9
Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang
-
split
的默認行為是用空白\s+
分割存放于$_
內(nèi)的字符串:
demo9-10
:
#!/usr/bin/perl
$_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
foreach (split){
print $_."\n";
}
稍有不同之處在于所有空字段都被舍棄(因為連續(xù)的空白字符本身就匹配了\s+
模式,所以不存在字符串內(nèi)部的空字段):
./demo9-10
Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang
標(biāo)準的寫法可能是 split /\s+/, $_;
demo9-11
:
#!/usr/bin/perl
$_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
foreach (split /\s+/, $_){
print $_."\n";
}
這樣則可以保留字符串前面的空字段沮焕,別忘了連續(xù)的空白字符本身就匹配了\s+
模式穆趴,所以不存在字符串內(nèi)部的空字段
./demo9-11
Zhao
Qian
Sun
LI
Zhou
Wu
Zheng
Wang
- 和
split
操作符功能相反的是join
函數(shù)(沒錯,split
是操作符遇汞,但是join
是函數(shù))未妹,不過join
并沒有使用模式:
$result = join $glue, @pieces;
join
函數(shù)的功能是把@pieces
中的字符串片段用$glue
膠水粘合成一個字符串$result
,$glue
保存著充當(dāng)膠水的字符串而不是模式
join
可以和split
結(jié)合實現(xiàn)替換功能,效果類似s///g
:
demo9-12
:
#!/usr/bin/perl
$_ = " Zhao Qian Sun LI Zhou Wu Zheng Wang ";
@pieces = split;
print join "&", @pieces;
print "\n";
用&
替換了空格
./demo9-12
Zhao&Qian&Sun&LI&Zhou&Wu&Zheng&Wang
- 在列表上下文中使用
m//
會得到有趣的新特性:如果模式匹配成功空入,會返回所有捕獲變量的列表
demo9-13
:
#!/usr/bin/perl
$_ = "Zhang san Li si Wang wu ";
@pieces = /([A-Z][a-z]+)/g;
foreach (@pieces){
print $_."\n";
}
捕獲了([A-Z][a-z]+)
首字母大寫其余字母小寫的單詞:
Zhang
Li
Wang
如果有多個捕獲模式就會返回多個捕獲串:
demo9-14
:
#!/usr/bin/perl
$_ = "Zhang san Li si Wang wu ";
@pieces = /([A-Z][a-z]+)\s+(\w+)/g;
foreach (@pieces){
print $_."\n";
}
返回的列表里包含了所有捕獲串
./demo9-14
Zhang
san
Li
si
Wang
wu
特別地络它,如果捕獲的模式恰好是兩個,則可以用哈希結(jié)構(gòu)存儲m//
返回的結(jié)果歪赢,這種情況下化戳,第一個捕獲的變量是哈希的鍵,第二個捕獲的是哈希的值:
demo9-15
:
#!/usr/bin/perl
$input = "Zhang san Li si Wang wu ";
%hash = ($input =~ /([A-Z][a-z]+)\s+(\w+)/g);
foreach (keys %hash){
print $_."=>".$hash{$_}."\n";
}
./demo9-15
Wang=>wu
Li=>si
Zhang=>san
- 正則表達式中的量詞:
*, +, ?, {M,N}
默認都是貪婪量詞埋凯,即會盡可能多的匹配点楼,總會匹配到最大的字符串,而如果想要使用非貪婪量詞白对,只需要在原來的量詞后面加上?
:*?, +?, ??, {M,N}?
掠廓,這種情況下會盡可能少的匹配:
demo9-16
:
#!/usr/bin/perl
$_ = "TAG a b c d ef ghi jk TAG lmn opq TAG rst uvw xyz TAG";
if(m/TAG (.+) TAG/){
print "Greedy:".$&."\n";
}
if(m/TAG (.+?) TAG/){
print "Non-greedy:".$&."\n";
}
./demo9-16
Greedy:TAG a b c d ef ghi jk TAG lmn opq TAG rst uvw xyz TAG
Non-greedy:TAG a b c d ef ghi jk TAG
- 使用鉆石操作符的新特性,結(jié)合正則表達式甩恼,使用Perl來修改文件:
當(dāng)$^I
變量中是字符串時蟀瞧,將賦予鉆石操作符<>
新的特性:首先將@ARGV
中字符串對應(yīng)的文件改名,加上后綴条摸,后綴內(nèi)容即是$^I
內(nèi)存儲的值悦污,修改輸入指向這個文件,然后打開一個新文件钉蒲,命名為原來的文件名切端,修改輸出到這個新文件。
demo9-17
:
#!/usr/bin/perl -w
use strict;
$^I=".bak";
chomp(my $date = `date`);
while(<>){
s/Date:.*/Date:$date/;
print;
}
另外創(chuàng)建一個文件寫上“Date:”, 則每次運行程序顷啼,都會把Date后的時間更新為當(dāng)前時間
echo "Date: " > test
./demo9-17 test
這時目錄下生成一個test.bak
文件踏枣,存儲著修改前的內(nèi)容小压。 while
循環(huán)里每次都會從test.bak
里讀取一行放到$_
里,然后用s///
修改后椰于,打印到test
文件里(輸出已經(jīng)被<>
重定向到新文件里了)
如果$^I
為空怠益,則修改就會直接作用在源文件上
因為這個程序太普遍,所以有了它的簡化版本:
perl -p -i.bak -w -e 'chomp(my $date=`date`);' -e 's/Date:.*/Date:$date/' tes*
可以直接在命令行執(zhí)行瘾婿,作用和 demo9-17
相同
-p
選項讓perl自動生成一段類似下面這樣的小程序:
while(<>){
print;
}
-e
后面跟著程序代碼蜻牢,會插入到while循環(huán)之中
-i.bak
和$^I=".bak"
的作用相同,如果只有-i
那就像是把$^I
變量置為空偏陪,則不會有備份動作發(fā)生抢呆。
最后一個參數(shù)表示設(shè)定@ARGV
的值,這里使用了通配符