標籤:
1.使用Regex修改文本 1.使用Regex修改文本
Regex的功能不只有查詢,還可以對文本進行修改,例如替換
$var=~m/regex/i
$var=~s/regex/replacement/i
Replacement兩側的斜杠相當於雙引號,也就是說replacement中可以有$1,$2這樣的變數來代表前面匹配到的內容
用$var=~s/regex/replacement/
可以改變$var中的文本,如果沒有匹配成功,就不會有文本的替換
$var=Jeff frield;
$var=~s/Jeff/Jeffery/;
$var=Jeffery frield
可是如果在運行$var=~s/Jeff/Jeffery/
一次,就會得到:
$var=Jefferyery fried;
因為Jeff始終被匹配,所以替換始終會發生。
要避免這樣的情況,我們必須要把匹配條件說的再具體一些。
匹配的是Jeff這個單詞,而不是這四個字母,所以我們用:
$var=~s/\bJeff\b/Jeffery/
這樣子第二次運行$var=~s/\bJeff\b/Jeffery/
就不會再改變文本了
1.1例子一公函產生器
設想有一個公函系統,它包含很多公函魔板,其中一些標記,對每一封具體的公函來說,標記部分的值都有所不同
例如:
尊敬的=FIRST=,
恭喜您!獲得了=TRINKET=!完全免費!您還想為=FAMILY=家獲得更多的=TRINKEY=嗎?告訴你=FULL=,你可以的!
然後設定三個變數:
$given="王";
$family="小明";
$prize="10000克拉的鑽石";
然後我們就可以為語句的模板填寫內容:
$letter=~s/=FIRST=/$family/g;
$letter=~s/=FAMILY=/$given/g;
$letter=~s/=FULL=/$given $family/g;
$letter=~s/=TRINKET=/價值連城$prize/g;
g是全域替換的修飾符,他告訴s///在一次匹配替換成功以後繼續下一次,直到匹配不成功為止,從而達到全部相關文本都被替換的效果
結果:
尊敬的 小明,
恭喜您!獲得了 價值連城10000克拉的鑽石!完全免費!您還想為王家獲得更多的價值連城10000克拉的鑽石嗎?告訴你王小明,你可以的!
1.2 例子二修整數字格式
有時候因為電腦內部表示浮點的原理,輸出來的數字是9.05000000372272,可是我們其實只需要保留小數點後三位就可以了
要求就是:保留小數點後兩位,如果第三位不為0,也要保留,例如12.3750000000392會變成12.375,37.500會變成37.50.
$num=~s/(\.\d\d[1-9]?)\d*/$1/;
用環視功能為數值添加逗號
大的數值,為了方便讀懂,通常在其間加入逗號。
Print "the US population is $pop\n";
會輸出the US population is 298444215,但298,444,215會更加順眼。
我們應該從數位右邊開始,每次數三個數字如果左邊還有數字,就加入一個逗號。
這是直觀的想法,但是Regex是從左至右去處理文本的。
逗號應該加在”左邊有數字,右邊數位個數正好是3的倍數的位置“。
對於這樣的任務,我們用環視功能來實現。
環視結構不匹配任何字元,只匹配文本中特定的位置。這個特性其實我們前面已經見過很多次,例如\b、^、$等,都是匹配一個位置,但環視比它們更加通用,因為它匹配的位置是你自己定義的。
順序環視:從左至右查看文本,嘗試匹配Regex。肯定型順序環視用(?=……)來表示,例如(?=\d),表示如果當前位置右邊的字元是數字則匹配成功
逆序環視:從右至左查看文本,嘗試匹配Regex。肯定型逆序環視用(?<=……)來表示,例如(?<=\d),表示如果當前位置左邊字元是數字則匹配成功(也就是緊跟在數字後面的位置)。
環視Regex在匹配的時候不會“佔用”字元,只是匹配位置。
用Jeffery匹配by Jeffery friedl,匹配到是Jeffery這幾個字元之前的位置
把環視結構和真正匹配的字元結合起來使用,我們能匹配到更加精準的內容,例如:
(?=Jeffery)Jeff
能匹配到by Jeffery friedl
不能匹配by Jefferson
(?=Jeffery)Jeff ;Jeff(?=ery)
Jeff(?=Jeffery)不能夠匹配以上例子,而是匹配後面緊跟Jeffery的Jeff,例如JeffJeffery
1.3例子三effs=》Jeff’s
把Jeffs換成Jeff’s
s/Jeffs/Jeff’s/
s/\bJeffs\b/Jeff’ s/
s/\b(Jeff)(s)\b/$1’$2/
s/\bJeff(?=s\b)/Jeff’/
環視只是匹配一個位置,它的好處是容許我們在匹配Jeff前先檢查整個Jeffs
s/(?<=\bJeff)(?=s\b)/’/
s/(?=s\b)(?<=\bJeff)/’/
用順序環視和逆序環視找到了一個精確的位置,因為找的只是位置,所以條件循序調換了也沒有影響。
1.4例子四回到逗號
“左邊有數字,右邊數位個數正好是3的倍數”。
第一個要求用逆序環視就能夠滿足,左邊有數字,(?<=\d)
第二個要求:3位元字可以表示成\d\d\d
,然後可以用(\d\d\d)+
表示3的若干倍
最後再加上$來確保這些數字後面不存在其他字元,以保證這樣的結果“正好”在最後3位元字之後結束
$pop=~s/(?<=\d)(?=(\d\d\d)+$)/,/g;
Print"The US population is $pop\n";
298,444,215
試想,如果不加$,會有什麼後果?
$pop=~s/(?<=\d)(?=(\d\d\d)+)/,/g;
2,9,8,4,4,4,215
而且這裡括住\d\d\d的括弧,其實我們只是用來使得+可以作用於這個括弧,並沒有使用它的捕獲功能,所以可以寫成非捕獲型括弧:(?:......)
$pop=~s/(?<=\d)(?=(?:\d\d\d)+$)/,/g;
否定環視
現在,我們又希望把這個插入逗號的Regex應用到很長的字串匯中,例如
$text="The population of 299444215 is growing"
這樣子s/(?<=\d)(?=(\d\d\d)+$)/,/g;
就不管用了,因為數字之後不是結尾,所以匹配不成功
解決方案:可以把$換成\b,儘管\b被稱為單詞分隔字元,但是對於perl來說,匹配單詞的\w是[a-zA-Z0-9],把數字也包括進去了,所以這是廣義的單詞
注意到這裡,\b的意思就是,在此位置的一側是單詞,另外一側不是
環視也有相關的概念,前面我們說的(?=)
、(?<=)
都叫做肯定順序環視和肯定逆序環視。因為他們成功的條件是子運算式在這些位置能夠匹配
另外還有否定順序環視(?!)
和否定逆序環視(?<!)
,他們成功條件是子運算式無法匹配
類型 |
Regex |
匹配成功的條件 |
肯定順序環視 |
(?=) |
子運算式能夠匹配右側文本 |
肯定逆序環視 |
(?<=) |
子運算式能夠匹配左側文本 |
否定順序環視 |
(?!) |
子運算式不能夠匹配右側文本 |
否定逆序環視 |
(? |
子運算式不能夠匹配左側文本 |
這樣,其實\b就是(?<!\w)(?=\w)|(?<=\w)(?!\w)
s/(?<=\d)(?=(\d\d\d)+(?!\d)/,/g;
不是所有的宿主語言都支援逆序環視
那麼我們可以這樣子寫,這樣就沒有用到逆序環視
s/(\d)(?=(\d\d\d)+$)/$1,/g;
如果連順序環視都不用呢?
s/(\d)((\d\d\d)+\b)/$1,$2/g;
可以嗎?
答案是不可以,結果是298,444215
因為g這個修飾符規定,下一次匹配是在這一次匹配的終點開始的。但是在第一次匹配時,(\d\d\d)+\b已經匹配了444215,所以g的下一次匹配開始是在5的後面
解決方案是在perl中加個while迴圈,重複匹配,而不是迭代匹配
2.Regex使用注意事項
在某種特定的宿主語言或工具軟體中使用Regex時,主要有3個問題需要注意
1.支援的元字元,以及這些元字元的意義,這通常稱為Regex的“流派”
2.Regex與語言工具的“互動方式”。譬如如何進行Regex的操作,容許進行哪些操作,以及這些操作的目標文本類型
3.Regex引擎如何將運算式應用到文本
由於Regex的漫長的發展史,眾多程式員,新的程式又形成自己的流派,所以就成了巨大的迷局。
直到1986年,POSIX(一系列標準)誕生,它是標準化的嘗試,試圖把纏繞不清的Regex各個流派標準化,用同一套規則來實現Regex。它把各種常見的流派分為兩大類:
Basic Regular Expressions(BREs)和Extended Regular Expressions(EREs)
POSIX程式必須支援其中任意一種
perlRegex第二周筆記