無論哪種語言,在使用正則表達式的時候都避免不了一個問題,就是在匹配元字符的時候,需要對元字符進行轉義,讓
正則表達式引擎將其當做普通字符來匹配。本文主要以python為例,說明一下轉義中需要注意的問題。
python的正則表達式中需要轉義的元字符有以下幾個:
-
- .
- ^
- $
- *
- +
- ?
- \\
- []
- |
- {}
- ()
python中對元字符的轉義使用雙反斜杠 \\ 來表示
# 普通元字符的轉義 _string = ''' !@#$%^& ''' # 不轉義 print re.findall('$', _string) #>>> ['', ''] # 雙反斜杠轉義 print re.findall('\\$', _string) #>>> ['$'] # 單反斜杠轉義 print re.findall('\$', _string) #>>> ['$']
看上面的例子大家可能會發現,使用一個反斜杠 \ 也可以達到轉義的效果,那為什么還要寫兩個呢?這得先搞清楚python
的字符串轉義(不是正則表達式轉義),python本身使用 \ 來轉義一些特殊字符,比如在字符串中加入引號的時候,為了
s = 'i\'m superman' print s #>>> i'm superman
防止和字符串本身的引號沖突,使用 \ 來轉義,一般情況下這個也不會引起什么問題,但是當你要使用 \ 來轉義 \ 的時候,
就比較混亂了,比如我們想要輸出一個 \ ,得寫兩個 \ ,否則會報語法錯誤,因為 \ 把后面的引號給轉義了,必須使用 \
# 錯誤寫法 # print '\' # 正確寫法 print '\\' #>>> \ # 原生字符串 print r'\\' #>>> \\
將 \ 轉義一下使其不具備轉義功能,才可以正確輸出,當使用原生字符串的時候,輸出顯示了兩個 \ ,看起來好像是寫幾個
輸出幾個的樣子,如果這樣想的話,你可以試一下,看能不能輸出奇數個 \。
先來說一下什么是原生字符串,其實就是不進行特殊處理的字符串,所謂特殊處理,貌似就是針對轉義的,原生字符串
的誕生本身就是為了解決轉義的時候寫了太多 \ 的問題,但是為什么使用了原生字符串仍然不能只輸出一個 \ 呢?其實這應
該算是一個bug,就是python的字符串不能以奇數個 \ 結尾,這樣的寫法會被認為是將結尾的引號進行了轉義,導致語法錯
誤。具體可以參考http://t.cn/RfolM3H。
雖然原生字符串並不是很完美,但它已經可以幫我們解決很大一部分問題了。比如當你想匹配 \ 的時候,原生字符串可
_string = '\\\\' print _string #>>> \\ # 字符串 for i in re.findall('\\\\', _string): print i #>>> \ #>>> \ # 原生字符串 for i in re.findall(r'\\', _string): print i #>>> \ #>>> \
以讓你少寫一半的 \ ,既節省代碼量,又增加可讀性。
說了這么多也沒說為什么在寫正則表達式的時候一個 \ 也可以起到轉義的作用。我們先來分析一下一個字符串被正則表
達式引擎解析的過程,一共有4步:
-
- 首先正則表達式是一個python的字符串
- 字符串本身會先進行轉義處理
- 正則表達式引擎得到處理之后的字符串后再對字符串進行正則表達式引擎自己的處理
- 開始匹配
# 字符串 # '\\\\' # 經過python處理之后 # '\\' # 正則表達式引擎接收到的 # '\\' # 正則表達式引擎進行轉義處理后 可以匹配到 \ # '\'
而當使用原生字符串的時候就變為了3步
# 原生 # '\\' # 不再處理 # '\\' # 正則表達式引擎接收到的 # '\\' # 正則表達式引擎進行轉義處理 # '\'
下面是最重要的一個,當使用一個 \ 轉義的時候,python會識別不了轉義序列,於是它就不做任何處理,直接傳給了
正則表達式引擎。這就解釋了為什么一個 \也可以轉義。這個不算bug,雖然方便了使用,但會讓人很迷惑,有利有弊吧。
# 原生 # '\$' # 識別不了 不進行處理 # '\$' # 正則表達式引擎接收到的 # '\$' # 正則表達式引擎進行轉義處理 # '$'
下面舉幾個例子看一下
# 匹配 \d+ _string = 'i am \d+' print re.findall('\\\\d\\+', _string)[0] #>>> \d+ print re.findall(r'\\d\+', _string)[0] #>>> \d+ # 匹配 [] _string = 'i am []' print re.findall('\\[\\]', _string)[0] #>>> [] print re.findall('\[\]', _string)[0] #>>> [] print re.findall(r'\[\]', _string)[0] #>>> []