前言
平時寫腳本,經常會被正則匹配中的轉義弄得頭大,因此基於perl和python的字符串轉義和正則匹配轉義探究一下轉義的規則。
字符串轉義
腳本語言的字符串轉義基本是通用的,菜鳥教程中羅列了一下基本的轉義字符:
轉義字符 | 描述 |
---|---|
\(在行尾時) | 續行符 |
\\ | 反斜槓符號 |
\' | 單引號 |
\" | 雙引號 |
\a | 響鈴 |
\b | 退格(Backspace) |
\e | 轉義 |
\000 | 空 |
\n | 換行 |
\v | 縱向製表符 |
\t | 橫向製表符 |
\r | 回車 |
\f | 換頁 |
\oyy | 八進制數,yy代表的字符,例如:\o12代表換行 |
\xyy | 十六進制數,yy代表的字符,例如:\x0a代表換行 |
\other | 其它的字符以普通格式輸出 |
當使用轉義方式表達字符串時,便會呈現轉義效果,例如在python3中直接鍵入打印語句:
print('http:\\\\sscc.com\tcompile\\t.com')
打印效果如下:
由於python不通過 ‘ “ 來區分轉義符,因此以下兩種打印結果時一致的:
print('http:\\\\sscc.com\tcompile\\t.com')
print("http:\\\\sscc.com\tcompile\\t.com")
打印效果如下:
而perl是通過 ' ' 和 " ” 來區分字符串轉義,因此以下兩種揭發打印結果不同,“ 中字符串會進行字符串轉義, ' 中的字符串不會進行字符串轉義:
print "in \", lets print: http:\\\\sscc.com\tcompile\\t.com\n";
print 'in \', lets print: http:\\\\sscc.com\tcompile\\t.com\n';
打印效果如下:
不過直接說 “ " 中字符串會進行字符串轉義, ' ' 中的字符串不會進行字符串轉義也不完美,有特殊的情況,一是 \' \"這兩個轉義符都會轉義,二是 \\ 這個在兩個打印中都進行了轉義。
關於perl中 \\ 在 ' ' 和 " " 中的效果,我們看兩個例子。首先可以看下在 ' ' 中使用:
print 'in \', lets print: http:\sscc.com\tcompile\\t.com\n';
print "\n";
print 'in \', lets print: http:\\sscc.com\tcompile\\t.com\n';
print "\n";
print 'in \', lets print: http:\\\sscc.com\tcompile\\t.com\n';
print "\n";
print 'in \', lets print: http:\\\\sscc.com\tcompile\\t.com\n';
print "\n";
打印效果如下:
可以發現,\\被轉義爲\,\\\\被轉義爲\\,這個知其然就好了我沒有去查相關的說明。
其次看下在 " " 中轉義\\的效果:
print "in \", lets print: http:\sscc.com\tcompile\\t.com\n";
print "in \", lets print: http:\\sscc.com\tcompile\\t.com\n";
print "in \", lets print: http:\\\sscc.com\tcompile\\t.com\n";
print "in \", lets print: http:\\\\sscc.com\tcompile\\t.com\n";
打印效果如下:
可以發現這是很標準的字符串轉義,單個\使用時會跟下一個字符(串)檢查是否又轉義存在,如果不存在\直接不打印否則打印轉義後效果,\\一起使用時第一個\將第二個\轉義爲 不帶有任何轉義意義的反斜槓符號 本身,\\\一起使用第一個\將第二個轉義爲反斜槓符號 第三個和後面不構成轉義符因此不打印,\\\\就不分析了。
上面說的perl中的效果,python中不區分' '和" "的,那麼如何判定是否轉義呢?通過前綴符號r來區分,看下下面的代碼:
print(r"http:\sscc.com\tcompile\\t.com")
print(r"http:\\sscc.com\tcompile\\t.com")
print(r"http:\\\sscc.com\tcompile\\t.com")
print(r"http:\\\\sscc.com\tcompile\\t.com")
打印效果如下:
可以發現python對於字符串轉義隔離的很清晰,使用r前綴後,所以字符只表示本身而不帶有任何字符串轉義效果。
python中正則匹配轉義
下面還是摘以下菜鳥教程的表,正則匹配中常用轉義符有以下這些:
\w | 匹配字母數字及下劃線 |
\W | 匹配非字母數字及下劃線 |
\s | 匹配任意空白字符,等價於 [\t\n\r\f]. |
\S | 匹配任意非空字符 |
\d | 匹配任意數字,等價於 [0-9]. |
\D | 匹配任意非數字 |
\A | 匹配字符串開始 |
\Z | 匹配字符串結束,如果是存在換行,只匹配到換行前的結束字符串。 |
\z | 匹配字符串結束 |
\G | 匹配最後匹配完成的位置。 |
\b | 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非單詞邊界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\n, \t, 等. | 匹配一個換行符。匹配一個製表符。等 |
\1...\9 | 匹配第n個分組的內容。 |
\10 | 匹配第n個分組的內容,如果它經匹配。否則指的是八進制字符碼的表達式。 |
這個就不用細說了,我們主要探究的是字符串轉義和正則轉義混合時候的情況,先來看一組測試效果我們再來慢慢分析:
if re.search('\\\\d+', 'http:\d+baidu.com'):
print('match 1')
if re.search('\\d+', 'http:\d+baidu.com'):
print('match 2')
if re.search('\\d+', 'http:baidu360.com'):
print('match 3')
if re.search(r'\d+', 'http:baidu360.com'):
print('match 4')
if re.search(r'\d+', 'http:\d+baidu.com'):
print('match 5')
if re.search(r'\\d+', 'http:\d+baidu.com'):
print('match 6')
打印效果如下:
那麼我們來看下,從第一個和第二個一起看下,我想要用不帶前綴r的形式(即帶字符串轉義的若干字符)進行匹配字符串 \d+,第一種方式成功了而第二種沒有成功。
if re.search('\\\\d+', 'http:\d+baidu.com'):
print('match 1')
再分析下\\\\,前兩個\\通過字符串轉義變爲反斜槓符號\,後兩個\\同樣翻譯爲反斜槓符號\,那麼經過字符串轉義後變成了匹配\\d+,那麼再進行正則匹配轉義;正則匹配轉義中\\表示\(即不對後面進行正則轉義的普通反斜槓),那麼我們就成功的匹配到了後面字符中的\d+。
if re.search('\\d+', 'http:\d+baidu.com'):
print('match 2')
經過前面的分析就很容易得出第二個爲什麼無法匹配到\d+了,因爲經過字符串轉義後\\d+變成了\d+,而後進行正則轉義的時候\d+被轉義爲”1個或以上的數字“,因此自然無法匹配字符串\d+。
if re.search('\\d+', 'http:baidu360.com'):
print('match 3')
結合第三個看就更加明顯了,第三個就能夠匹配。
所以我們發現,如果字符串轉義和正則轉義參雜在一起,有的時候很繁瑣,因此一般我都會採用前綴r的方式使之忽略字符串轉義。
if re.search(r'\d+', 'http:baidu360.com'):
print('match 4')
if re.search(r'\d+', 'http:\d+baidu.com'):
print('match 5')
if re.search(r'\\d+', 'http:\d+baidu.com'):
print('match 6')
忽略字符串轉義的效果就如同後三個實驗一樣了,\d+直接翻譯爲”1個或以上的數字“去匹配後面的數字,\\d+纔會匹配到後面的字符串\d+。
上面的實驗是匹配是否帶前綴r的問題,其實後面一樣可以帶前綴r,看個例子就就明白了:
if re.search(r'\\n', r'http:\nbaidu.com'):
print('match 7')
if re.search(r'\\n', 'http:\nbaidu.com'):
print('match 8')
打印結果如下:
可見,當被匹配項有前綴r的時候,其中的\n沒有被翻譯爲換行,因此可以被\n匹配到;而不加r的話就被字符轉轉義翻譯爲換行,那麼顯然不能被匹配到了。
perl中正則匹配轉義
perl的效果我們直接看例子:
$line1 = "http:baidu360.com";
if ($line1 =~ /\d+/){
print("match 1\n")
}
$line2 = "http:baidu\d+.com";
if ($line2 =~ /\d+/){
print("match 2\n")
}
$line3 = 'http:baidu\d+.com';
if ($line3 =~ /\\d+/){
print("match 3\n")
}
打印效果如下:
可以發現,就是 /匹配項/ 默認採用的是忽略字符串轉義,前面的被匹配項根據你賦值時候的是使用的' '還是" "有所區別," "的時候會先把被匹配項做字符串轉義。
結語
實驗做累了,就寫到這吧。
總結來說呢,perl中的匹配項默認是忽略字符串轉義的,python中根據有無前綴r來判定。
而shell和tcl我記得是默認帶着字符串轉義的,所以總看到shell裏面各種/\\d+\\w+/什麼的,不知道有沒有記錯。