[PERL] 16-字串取代和置換

URL Link //n.sfs.tw/11848

2017-09-27 21:23:54 By 張○○

PERL的字串比對置換非常的強大也複雜,常容易搞錯

字串的取代語法

s/原來字串/目的字串/修飾子

  s 是功能代號 substitute的意思
  原來的字串和目的字串都能使用正規表達式
  修飾子能讓取代得到更多彈性

單筆取代

把Who取代為What

$str = "Who are you? Who is he?";
$str =~ s/Who/What/;

print $str; # What are you? Who is he?

第2行 使用比對運算子'=~'來作字串的取代,如果寫成等於 '=' 就不會有任何結果。

這樣的結果只會取代第一個發現的目標。

 

大量取代

如果要一次取代多組比對結果,可用前面介紹過的 'g' 修飾子:

$str =~ s/Who/What/g;

這樣一次就能全部取代

大量取代有一個問題就是想知道到底取代多少筆,有一個技巧能取得取代的個數:

$n= $str =~ s/Who/What/g;
print $n; # 取代的個數

 

取代時使用函數變換

s/// 裡面的「目的字串」是可以放入函式的,例如以下幾個常用的函數:

  uc($str) 把$str 全轉成大寫

  lc($str) 把$str 全轉成小寫

  ucfirst($str) 把$str 第一碼轉成大寫

要使用函式還得加上 'e' 修飾子才行,例如把下面的句子中每個單字w字首大寫

$str = "What a wonderful wonderful world.";
$str =~ s/w\w+/ucfirst($&)/ge;

print $str;

說明

第2行 樣式w\w+是指w開頭的字後面跟著\w+字元(a-zA-Z0-9_);ucfirst($&) 中的 $&是比對的結果

結果

What a Wonderful Wonderful World.

上面的 'e'修飾子不加的話會變成一般的字串。

函數也可以自己寫,例如我寫一個 'l' 轉成 '1'、'o' 轉成 '0'、'd'轉成'6'的函數 trans2pwd。

sub trans2pwd{
  my $s= shift;
  $s =~ tr/lod/106/;
  return $s;
}

$str = "What a wonderful wonderful world.";
$str =~ s/\w+/trans2pwd($&)/ge;

print $str;

說明

第1~5行 自訂函數trans2pwd
第3行 tr///這個是清單的取代,可以快速的作字元替換,接下來會談到。

結果

What a w0n6erfu1 w0n6erfu1 w0r16.

不得不說PERL實在太厲害了!

 

改變區隔字元

在取代中常常會遇到特殊字元需要作置換,例如網址 http://old.example.com/ 要換成 http://n.sfs.tw/old

這樣的寫法肯定是錯的

$str = "http://old.example.com/";
$str =~ s/http://old.example.com//http://n.sfs.tw/old/;

你必須要在 '/' 和 '.' 加上脫逸字元

$str =~ s/http:\/\/old\.example\.com\//http:\/\/n.sfs.tw\/old/;

這時你可以換掉區隔字元 '/' 為 '|',像這樣:

$str =~ s|http://old\.example\.com/|http://n.sfs.tw/old|;

這個區隔字元可以換成幾個不造成你干擾的字元都可以

s|原字串|新字串|
s#原字串#新字串#
s!原字串!新字串!
s(原字串)(新字串)
s{原字串}{新字串}

看起來簡潔多了。

 

使用清單來取代

上面的 s///是字元取代,如果想一次多項取代,可以使用PERL提供的清單取代 tr///

tr/原來比對清單/目的比對清單/選項

其中的選項只有三種

c Complement the SEARCHLIST.  <== 清單沒寫到的就補給他右邊清單的最後一個字元
d Delete found but unreplaced characters. <== 對照表中沒有的項目就刪掉
s Squash duplicate replaced characters. <== 連續重覆出現的字壓成一個

 

大小寫互換範例

如果要大小寫互換,對PERL來說容易

$str = "What a Wonderful Wonderful World.";
$str =~ tr/a-zA-Z/A-Za-z/;
print $str; 

清單互換可以這樣看成同色的互換,所以兩邊組數要一樣,以免產生非必要的結果。

tr/a-zA-Z/A-Za-z/

 

使用別名y

tr/// 還有一個別名,叫作 y/// 所以要把數字0和9互換,可以寫成:

$doc =~ y/09/90/;

y///和m//一樣,可以把邊界分隔字元換掉:

$doc =~ y|09|90|;

 

右清單數量不足

如果清單左右的數量不同,那麼PERL自動會補右邊清單最後一個字元,例如:

tr/a-zA-Z/A-Za-z/

如果只寫

tr/a-zA-Z/A-Z/

右邊清單少了一項,會發生什麼事?其實預設會補右邊清單A-Z的最後一個字元'Z',也就是說,左邊大寫的A-Z全換成'Z'

$str = "What a WonderFul World is to BE.";
$str =~ y|a-zA-Z|A-Z|;

結果
ZHAT A ZONDERZUL ZORLD IS TO ZZ.

 

使用 \w \d等集合字元沒有效果

清單需用列的,如果用集合字元 \d \w \W 等等不會有效果,例如

$str = "What a WonderFul World is to BE.";
$str =~ tr/\w/0-9/;

沒有作用,那是因為 '\' 字元在清單中是沒有作用的,只被當成一個普通的脫逸 '\' 字元。如果要取代的是反斜線本身,就得寫成 '\\'

 

使用選項c

選項c的效果是指如果左清單沒列到的,就全補右清單的最後一個字元,例如:

$str = "What a WonderFul World is to BE.";
$str =~ y|a-zA-Z|A-Z|c;

結果

WhatZaZWonderFulZWorldZisZtoZBEZ

左清單沒列到的 空白' '和 點'.'都換成大寫的Z了,原來該作的置換沒有做!其實就是例外處理的意思:

$str =~ y|a-zA-Z|Z|c;

 

使用選項d

選項d的效果是指如果右清單沒列到的,就刪掉,例如:

$str = "what a WonderFul World is to BE.";
$str =~ y|aeiou|AEI|d;

結果

whAt A WndErFl Wrld Is t BE.

其中aei三個字元換成AEI,但是ou這兩個字元沒有批配的清單內容,就被刪了。

 

使用選項s

重覆出現的字元壓成一個,例如:

$str = "Wooooo! good     cheese~~";
$str =~ y|oe |oe |s;

結果

Wo! god chese~~

重覆出現的 'o', 'e' ,' ' 換成單一一個 'o', 'e' ,' ' ,這個方法可以快速的移掉多餘的空白。

 

利用清單來計算字元數

清單可以拿來快速的計算字元數,只要善用PERL的邏輯就好。

如果置掉時,把自己換成自己,就知道有幾個字元了。例如計算數字出現個數:

$doc="<78>Nov  3 11:20:01 163.17.44.1 crond[30367]: (root) CMD (LANG=C LC_ALL=C )";
$n= $doc=~ tr/0-9/0-9/;
print $n; #22

左清單的0-9你可以換成任何想算的字元

 

在取代中使用變數

有些情況是想動態的讓樣式變化,無論是 s///或是tr/// 如果要用變數,寫成這樣:

$doc="<78>Nov  3 11:20:01 163.17.44.1 crond[30367]";
my $leftlist="a-zA-Z";
$_=$doc; # 用 $_ 設定預設的取代字串
eval "tr/$leftlist/ /, 1" or die $@; #把a-zA-Z換成空白
print $_."\n" ;

結果

<78>    3 11:20:01 163.17.44.1      [30367]

 

綜上所述,PERL的字元取代非常的好用,相信你會喜歡。

 

上一篇 15-進階比對 #2--使用更多修飾子
回到目錄 01-撰寫第一隻PERL程式
下一篇 17-參照