小馬哥正在針對Python的所有常見知識進行匯總,更會有大量實戰(zhàn)項目不斷補充進來.
點擊-->全棧工程師養(yǎng)成---Python內(nèi)容導航頁<--查看所有Python內(nèi)容
小馬哥在使用正則表達式面對轉(zhuǎn)義字符的時候,發(fā)現(xiàn)字符串的轉(zhuǎn)義與正則的轉(zhuǎn)義有沖突的情況,總一些雖說不難,但是讓人感覺沒規(guī)律的地方,于是就專門研究了一下,首先捋一下知識點,約定一個前提概念:
字符中的轉(zhuǎn)義: 屬于字符串的概念,作用于打印. 例如,要完成一個退格,一個制表符,一個換行,這個用字符串如何表示?
簡單,"\b,\t,\n",不過是用"\"來轉(zhuǎn)義一下普通字符嘛,
是的,因為b,t,n是普通字符,如果直接輸出,當然就是字符,于是加上"\",
打印的時候就是另一種意思了,這個打印,可以是輸出到屏幕,也可以是其它輸出
正則表達式中的轉(zhuǎn)義: 看到這里,你會想起,正則里面好像也有轉(zhuǎn)義的概念,例如,"."代表匹配字符串里面的任意字符(換行符除外),如果需要匹配真正的"."這個字符怎么辦?于是我們用轉(zhuǎn)義字符"."就代表匹配字符串中的"."了.
你有沒有想過這個問題,字符串中的轉(zhuǎn)義和正則中的轉(zhuǎn)義不是一套概念體系,如果兩者沒有表達上的沖突,那么相安無事,可是狀況恰恰是有沖突的,"\b"在字符串中表示轉(zhuǎn)義后的結(jié)果: 退格;而在正則中代表匹配目標字符串的邊界. 于是就要想辦法解決沖突: 定義了原始字符串來屏蔽掉沖突.
只看說明,你還是一頭蒙的,那么看例子來闡述一遍上面所有的問題:
首先,字符串的轉(zhuǎn)義
# 字符串中的轉(zhuǎn)義: 斜杠\代表轉(zhuǎn)義字符,\b代表轉(zhuǎn)義之后的字符串,是退格
data = '123\b456'
# 顯示一下結(jié)果
print(data) #發(fā)現(xiàn)被轉(zhuǎn)義后: 12456
data = '123\n456'
print(data) #換行: 就是通過轉(zhuǎn)義符把n進行轉(zhuǎn)義表示回車
123456
123
456
如上還有\(zhòng)t制表符也是同樣的意思,這些,都是來自于ASCII對字符的規(guī)定,記住: 這些屬于字符串中的轉(zhuǎn)義
然后,正則的轉(zhuǎn)義
import re
print('第一次試驗')
data = 'hello1 python,hello2 java'
pattern = '\bhello.'
result = re.findall(pattern,data)
print(result)
'''
分析:
這個時候的正則表達式模式,沒有匹配到期望的hello1與hello2,因為\b這里代表的不是正則的邊界
1,pattern = '\bhello.',首先是一個字符串,就要接受字符串的處理,
根據(jù)ASCII表,\b是退格,是一個看不到的字符,所以'\b'是一個字符,hello是另外的字符,
2,我們期望的正則模式是: '邊界hello.',這個"邊界"的表示方法和字符的ASCII碼沖突,沒有體現(xiàn)出來
于是,清楚了原因,我們應(yīng)該設(shè)計一個模式,在經(jīng)歷字符串ASCII解析后,再傳遞給正則引擎之后是\bhello
應(yīng)該這樣寫: '\\bhello'
'''
print('第二次試驗')
data = 'hello1 python,hello2 java'
pattern = '\\bhello.' #這時,經(jīng)過字符的ASCII表解析后,得到的字符串是'\bhello'
result = re.findall(pattern,data)
print(result)
'''
到此,你心里應(yīng)該有了一個: 計算機內(nèi)部針對 字符 - ->模式 的過程
不是你寫了一個字符串,它就是正則的模式了,而是計算機內(nèi)部要根據(jù)ASCII碼進行了解析,解析的結(jié)果,拿給了正則引擎去用來搜索目標文本,
而ASCII碼解析后的結(jié)果,和你期望的正則的模式發(fā)生變化了,所以正則表達式解析來的結(jié)果,不如我們期望的
'''
第一次試驗
[]
第二次試驗
['hello1', 'hello2']
如果通過上面的例子,你懂了ASCII碼在暗中干了一件'見不得人'的事,如果你的腦力還夠用,那么咱們再來看一個它的更有意思的事情
需求: 'python\java\go\c',解析出里面的" \ ",在字符概念領(lǐng)域,它是轉(zhuǎn)義字符,在正則領(lǐng)域,它也是轉(zhuǎn)義字符,如何操作呢?
上面,我們主要目的:讓你知道,ASCII碼和正則,針對轉(zhuǎn)義是不同的,有沖突的,例如,\b在兩者概念體系中就沖突了,
import re
data = 'python\java\go\c'
'''
pattern1
這個都不用試驗,肯定是錯的,語法錯誤,解析都通不過,相當于,你想打印" ' "單引號,但是字符串的定義就是用單引號或者雙引號來表示
\'相當于失去了字符串"'"的定義作用,而把"'"看做普通字符,那么這樣,字符串的pattern1,就沒有了字符串的結(jié)尾符了,必然錯誤
'''
# pattern1 = '\' #必須注釋了,語法都通不過檢查
'''
pattern2
用上一個例子中分析的結(jié)果,我們用轉(zhuǎn)義字符把斜杠處理過,得到就是ASCII處理后的"\",再拿去交給正則引擎,這個時候正則我們應(yīng)該沒問題了
的確,這里經(jīng)過ASCII碼處理,最終交給正則的是"\",這個時候就該正則引擎起作用了,對于它"\"也是一個轉(zhuǎn)義符,可只要\這是一個不完整的轉(zhuǎn)義
于是拋出error: bad escape (end of pattern) at position 0
'''
try:
pattern2 = '\\'
print(pattern2)
result2 = re.findall(pattern2,data)
print(result2)
except Exception as e:
print("Error: ",e)
'''
pattern3
我們應(yīng)該傳遞給正則解析引擎一個"\\",經(jīng)過正則處理,它會得到"\"這個僅僅代表字符的模式,拿著去和目標文本比對,搜索目標"\"
'\\\\'經(jīng)過ASCII碼處理,得到"\\",正則引擎處理'\\',得到'\'
'''
pattern3 = '\\\\'
result3 = re.findall(pattern3,data)
print(result3)
\
Error: bad escape (end of pattern) at position 0
['\\', '\\', '\\']
總結(jié)上面兩個例子
例一,你應(yīng)該明白了,一個字符串能作為正則的模式,中間是經(jīng)過ASCII碼轉(zhuǎn)換的,但是為什么"\d","\w"等就相安無事呢?因為ASCII碼沒"整"它們;
例二,對于""這種比較特殊的東東,經(jīng)過ASCII和正則的兩次轉(zhuǎn)義,語法都要正確,才能去匹配目標文本中的目標字符
有沒有一種方式,不用這么麻煩,于是出現(xiàn)了原始字符串.
原始字符串的用法:解除字符與正則關(guān)于轉(zhuǎn)義的沖突
import re
data = 'python\java\go\c'
pattern = r'\\' #這里為什么還要是兩個斜杠呢? 畢竟這里是字符串的定義
print(pattern)
result = re.findall(pattern,data)
print(result)
data = 'hello1 java,hello2 python'
pattern = r'\bhello.' #這里就去掉了\b的退格作用
print(pattern)
result = re.findall(pattern,data)
print(result)
\\
['\\', '\\', '\\']
\bhello.
['hello1', 'hello2']
可能有的同學有疑問: 為什么上面打印的result關(guān)于findall結(jié)果的地方,result列表里面都是'\',其實'\'代表的是一個字符'\',print()打印這個動作就變成''了.不信去試試.
小馬哥正在針對Python的所有常見知識進行匯總,更會有大量實戰(zhàn)項目不斷補充進來.
點擊-->全棧工程師養(yǎng)成---Python內(nèi)容導航頁<--查看所有Python內(nèi)容