python re正则查找_python正则表达式 - re
1,匹配符號
基本元字符
. : 任意字符,除了\n,flags設置為DOTALL(S)可以讓.匹配\n
|:邏輯或
\:轉義
():捕獲組
空白字符
[\b] : 回退
\f : 換頁
\n : 換行
\r : 回車
\t : 制表tab
\v : 垂直制表
特定字符
\d : 數字,等價[0-9]
\D : 非數字,等價[^0-9]
\w : 字母或數字或_,等價[A-Za-z0-9_]
\W : 非字母非數字非_,等價[^A-Za-z0-9]
\s : 空白字符,等價[\f\n\r\t\v]
\S : 非空白字符,等價[^\f\n\r\t\v]
重復匹配
+ : 一個或多個
* : 零個或多個
? : 零個或一個
{n} : n次
{n,} : 最少n次
{n,m} : 最少n次,最多m次
備注:+和*是貪婪型,有多少匹配多少,加上?可以變懶惰型
位置匹配
\b : 單詞邊界,即\w和\W之間的位置
\B : 非單詞邊界
^ : 字符串開頭,flags設置為MULTILINE(M)可以匹配\n后的位置
$ : 字符串結尾,flags設置為MULTILINE(M)可以匹配\n前的位置
\A:字符串開頭
\Z:字符串結尾
?=:向前查找
?<=:向后查找
2,字符集
字符集[]的規范/元字符不同于正則式主體
字符集中的特有元字符:
-:連字符,表示范圍,如果不想表示范圍必須放最前面
^:對字符集取反,作用于給定字符集內所有的字符或范圍,而不是僅限于^后面的一個字符或區間
正則表達式主體中的元字符,在字符集中無需轉義的(轉義了也可以,pycharm提示多余的):
(
)
.
*
?
匹配舉例:
[0-9] : 數字
[A-Z] : 大寫字母
[a-z] : 小寫字母
[A-Za-z0-9] : 字母或者數字
[^0-9] : 非數字
[.(] : .或(,[]字符集合內的元字符無需轉義
[-.\s]:-或.或空白字符,"-"如果不想表示范圍必須放最前面
[\n\d\s]: 匹配換行符或數字或空白
備注:如果想通過字符集[]匹配轉義符\,則正則表達式引擎必須接受字符\\:
print('a[\]b') # a[\]b
print('a[\\]b') # a[\]b,和單\結果一樣,因為單\會自動轉義,詳見后面的轉移章節說明
print(r'a[\\]b') # a[\\]b,OK!
3,匹配flags
MULTILINE(M):改變“^”和“$”,讓“^”能夠匹配到換行符后的位置,讓“$”能夠匹配到換行符前的位置
DOTALL(S):改變“.”,讓“.”能夠匹配換行符
IGNORECASE(I):忽略大小寫
LOCALE(L)
UNICODE(U)
VERBOSE(X)
ASCII(A)
re.M可以使得^和$匹配'\n'后和'\n'前得位置,例如:
用'\n'也可以匹配結果,但是'\n'不是位置匹配,會把'\n'也匹配在內。
>>> s = 'aaa\nbbb\nccc'
>>> r = re.search('^bbb$', s)
>>> r # 匹配不到
>>> r = re.search('^bbb$', s, re.M)
>>> r.group()
'bbb'
>>> r = re.search('\nbbb\n', s, re.M)
>>> r.group()
'\nbbb\n'
總結:經過re.M改造后的^和$,相當于是向前查找和向后查找結合了‘\n’,例如:
s = 'start\nhello guxh2\nend'
print(s)
"""
start
hello guxh2
end
"""
print(repr(s))
'start\nhello guxh2\nend'
>>> re.findall(r'^hello .*?$', s, re.M)
['hello guxh2']
>>> re.findall(r'(?<=\n)hello .*?(?=\n)', s)
['hello guxh2']
>>> re.findall(r'\nhello .*?\n', s) # 不加定界會匹配上\n
re.findall(r'\nhello .*?\n', s)
4,匹配模式
整體匹配(只匹配第一次):re.search / re.match
全局匹配(匹配所有):re.findall
分組捕獲:(),整體匹配和全局匹配都可以進行分組捕獲,捕獲匹配結果中的部分內容,分組捕獲可以嵌套,詳見匹配舉例。分組可以嵌套,匹配結果會含大分組和子分組的結果。
5,匹配寫法
方法一,需要重復執行匹配的,先編譯再匹配
好處是多個地方用到這個匹配模式regex,想修改時只需要修改一個地方
regex = re.compile(p)
regex.search(s)
re.search(regex, s)
方法二,簡單的可以直接匹配
re.search(p, s)
6,python提供的匹配方法
1)re.search(p, s)
從s中提取完全符合p的內容,只提取第一次命中的,返回re.match對象
提取整體匹配結果:searchObj.group(), searchObj.group(0)
提取所有分組捕獲結果:searchObj.groups()
提取單個分組捕獲結果:searchObj.group(n), 即groups()[i] = group(i+1)
2)re.findall(p, s)
提取所有符合p的內容,返回字符串組成的列表
使用分組捕獲時,返回分組捕獲結果(元組)組成的列表
re.search使用分組捕獲時既可以捕獲整體匹配,也可以捕獲分組內容
re.findall使用分組捕獲時只能捕獲分組內容
3)re.match(p, s)
可以用re.search(^)替代
MULTILINE模式下,match也只匹配s,但re.search可以匹配換行后的開始
4)re.finditer(p, s)
re.findall的惰性版,返回iterator
5)re.split(p, s)
根據p分割字符串s
6)re.sub(p, repl, s, count=n)
p是匹配到的值,repl是替換后值,s是m目標字符串,count是替換多少次
re.subn(p, repl)可以返回替換次數
7,懶惰匹配和貪婪匹配
*和+都是貪婪比配,盡可能多
可以結合?變成懶惰匹配
>>>re.search('a.*?b', 'abab').group()
ab
>>>re.search('a.*?b', 'abab').group()
abab
>>>re.search('192\.168\.1\..*$', '192.168.1.12').group()
192.168.1.12
>>>re.search('192\.168\.1\..*?', '192.168.1.12').group() # 沒有結束符,懶惰不匹配
192.168.1.
>>>re.search('192\.168\.1\..*', '192.168.1.12').group() # 沒有結束符,貪婪匹配至最后
192.168.1.12
8,轉義
1)python語言的轉義
'\n'在python中代表換行,print輸出時會做特殊處理:
s = 'x\nz'
print(s) # x(換行)z
print(repr(s)) # 'x\nz'
要想print輸出'x\nz',需要對'\'進行轉義:
s = 'x\\nz' # 或s = r'x\nz'
print(s) # x\nz
print(repr(s)) # 'x\\nz'
如果'\' + '字符'沒有特殊含義,python語言會自動對\進行轉義:
s = 'x\yz'
print(s) # x\yz
print(repr(s)) # 'x\\yz',發現多了個'\'
自動轉義時,PEP8會提示invalid escape sequence '\y',看來PEP8要求'\'+'字符'沒有特殊含義時,統一做好轉義!
2)正則表達式的轉義 - 非元字符
傳遞給正則引擎的字符串regex的內容,是print(regex)的結果,而不是print(repr(s))的結果
假設s = 'x\yz',print(s)的結果是'x\yz',print(repr(s))的結果是'x\\yz',傳遞給正則表達式的是'x\yz'
匹配時,也是匹配對象字符串s的print(s)的結果
和編程語言的轉義一樣,正則引擎也有自己的轉義規則,當'\' + '字符'屬于元字符時,可以進行特殊處理,例如\w匹配字符,數字,以及'_'
但是如果'\' + '字符'不是元字符,即沒有特殊含義時,正則引擎不會像編程語言那樣自動進行轉義,而是要求必須接收轉義后的'\',例如:
s = r'x\yz'
regex = 'x\\yz' # 或者regex = 'x\yz',print(regex)都是'x\yz',傳遞給正則引擎的就是'x\yz'
re.search(regex, s) # re.error: bad escape \y at position 1
這樣的字符串傳遞給正則引擎才能得到正確處理:
regex = r'x\\yz' # print(regex)是'x\\yz',print(repr(regex))是'x\\\\yz'
regex = 'x\\\yz' # 同上
regex = 'x\\\\yz' # 同上
為什么'x\\\yz'和'x\\\\yz'結果一樣呢?因為對于'\\\y',前2個'\\'轉義為'\',后面的'\y',python語言自動轉義成了'\y'(同樣也會收到PEP8提示)
3)正則表達式的轉義 - 元字符
假設要匹配的字符串為'a(換行)b',即:
s = 'a\nb'
傳遞給正則表達式的字符串regex,經過轉義其print(regex)結果應當為'a\nb',于是匹配寫法為:
regex = r'a\nb' # 或者regex = 'a\\nb'
re.search(regex, s) # 可以匹配中s
但是發現未經轉義的'a\nb'也能匹配中(換成制表符'\t'也能匹配):
regex = 'a\nb'
re.search(regex, s) # 也能匹配中
這是因為給字符串傳遞'a(換行)b'時,字符串恰好會將其識別為'\n',交給正則引擎時就能夠正確去匹配'a(換行)b'
但是如果python語言字符串和正則引擎對字符的解釋不一樣時,轉義或者不轉義效果就會不同,例如:
s = 'a*'
regex1 = 'a\x2A'
regex2 = 'a\\x2A'
re.search(regex1, s) # 匹配結果為a
re.search(regex2, s) # 匹配結果為a*
對于'\x2A'(即*),正則表達式解釋為匹配前一個字符0次,1次或多次。
如果和\n一樣,則不管是'a\x2A'還是'a\\x2A'都能匹配中'a*'
但實際上,不轉義'\'就無法匹配'a*',那是因為不轉義的'*'在正則表達式中是元字符,被賦予了其他含義。而'*'在python語言字符串中卻沒有特殊含義。
9,捕獲組
1)捕獲組對現有匹配方法對影響
捕獲組對re.findall的影響:
s = 'abcabcabc'
re.findall('abc', s) # ['abc', 'abc', 'abc']
re.findall('a(b)c', s) # ['b', 'b', 'c']
re.findall('(a(b)c)') # [('abc', 'b'), ('abc', 'b'), ('abc', 'b')]
捕獲組對re.sub的影響:
s = 'abcabcabc'
re.sub('abc', 'x', s) # xxx
re.sub('a(b)c', 'x', s) # xxx
re.sub('(a(b)c)', 'x', s) # xxx
備注:看上去對sub不起作用,但捕獲組可以用來回溯引用
2)捕獲組的回溯引用
s = '
hello world
'
re.search(r'.*?', s) # 可以匹配s
re.findall(r'.*?', s) # ['h1']
re.sub(re.sub(r'(.*?)', r'\1,\2', s)) # h1, hello world,re.sub會對匹配中的整個字符串進行替換
3)捕獲組的命名
>>>ID = "310115199012128765"
>>>p = "(?P[0-9]{6})(?P[0-9]{8})"
>>>re.search(p, ID).groupdict()
{'dictrict': '310115', 'birthday': '19901212'}
10,前后查找
前后查找屬于位置匹配
(?<=s):向后查找,s后面的位置
(?=s):向前查找,s前面的位置
>>>s = 'ab1c ab2c\nab3c'
>>>re.findall('(a.*?)(?:\s|$)', s) # $不能放字符集[]里面
['ab1c', 'ab2c', 'ab3c']
>>>re.findall('((?<=b).*?)(?:\s|$)', s) # b后面的位置開始
['1c', '2c', '3c']
>>>re.findall('((?=b).*?)(?:\s|$)', s) # b前面的位置開始
['b1c', 'b2c', 'b3c']
前后查找和findall:
即使正則表達式捕獲的是位置,但是有了捕獲組以后,findall會返回捕獲組的內容,參考之前的re.findall('a(b)c', s)
s = '1234567890'
re.findall(r'(?<=\d)(?=(\d\d\d)+$)', s) # ['890', '890', '890'],實際上正則表達式匹配的是三個位置,但有了捕獲組以后findall的返回結果是捕獲組內容
re.findall(r'(?<=\d)(?=(?:\d\d\d)+$)', s) # ['', '', ''],改為非捕獲型后,findall返回的是三個位置:1|234|567|890
可以在該位置插入字符:
r = re.sub(r'(?<=\d)(?=(?:\d\d\d)+$)', ':', s) # 1:234:567:890
前后查找和sub:
sub是對匹配到的內容進行替換,捕獲組不會對替換結果產生影響,捕獲組在sub中只是用戶回溯引用,參考9捕獲組中的回溯引用
re.sub(r'(?<=\d)(?=(\d\d\d)+$)', '|', s) # 1|234|567|890,對匹配到的內容(位置)進行替換
re.sub(r'(?<=\d)(?=(?:\d\d\d)+$)', '|', s) # 1|234|567|890,對匹配到的內容
11,匹配舉例
'ab1c ab2c\nab3c'匹配例子:
>>>s = 'ab1c ab2c\nab3c'
>>>re.search('a.*?c a.*?c', s).group() # 整體匹配
ab1c ab2c
>>>re.search('a(.*?)c a(.*?)c', s).groups() # 整體匹配 + 捕獲分組
'b1 b2'
>>>re.search('a(\w(\w))c a(\w(\w))c', s).groups() # 整體匹配 + 捕獲分組
('b1', '1', 'b2', '2')
>>>re.findall('a.*?c', s) # 全局匹配
['ab1c', 'ab2c', 'ab3c']
>>>re.findall('a(.*?)c', s) # 全局匹配 + 捕獲分組:只能捕獲分組內容
['b1', 'b2', 'b3']
>>>re.findall('a(\w(\w))c', s) # 全局匹配 + 捕獲分組嵌套
[('b1', '1'), ('b2', '2'), ('b3', '3')]
>>>re.findall('a.*?c$', s)
['ab3c']
>>>re.findall('a.*?c$', s, flags=re.M) # re.M影響$
['ab1c ab2c', 'ab3c']
>>>re.findall('^a.*?c', s)
['ab1c']
>>>re.findall('^a.*?c', s, flags=re.M) # re.M影響^
['ab1c', 'ab3c']
>>>re.search('a.*c', s).group()
'ab1c ab2c'
>>>re.search('a.*c', s, flags=re.DOTALL).group() # re.DOTALL影響.
'ab1c ab2c\nab3c'
>>>re.split('\s', s)
['ab1c', 'ab2c', 'ab3c']
>>>re.split('(\s)', s) # re.split + 分組可以看到分隔符
['ab1c', ' ', 'ab2c', '\n', 'ab3c']
'a b, c ; d\ e'匹配例子:
>>>s = 'a b, c ; d\ e'
>>>s
'a b, c ; d\\ e' # \被自動轉成\\
>>>re.split(r'\s*[\s,;\\]\s*', s)
['a', 'b', 'c', 'd', 'e']
>>>re.split(r'\s*(?:\s|,|;|\\)\s*', s) # []的等效寫法
['a', 'b', 'c', 'd', 'e']
>>>re.split(r'\s*(\s|,|;|\\)\s*', s) # 不用?:只抓到了單個分隔符,用它去對s分割會包含單個分隔符
['a', ' ', 'b', ',', 'c', ';', 'd', '\\', 'e']
>>> re.split(r'(\s*(?:\s|,|;|\\)\s*)', s) # 包含完整的分隔符
['a', ' ', 'b', ', ', 'c', ' ; ', 'd', '\\ ', 'e']
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的python re正则查找_python正则表达式 - re的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux dhcp服务软包,dpkg包
- 下一篇: python中res代表什么_在下面的代