正則表達(dá)式

正則表達(dá)式

一、概述

1. 概念

正則表達(dá)式是對(duì)字符串操作的一種邏輯公式,就是用事先定義好的一些特定字符、及這些特定字符的組合,組成一個(gè)“規(guī)則字符串”,這個(gè)“規(guī)則字符串”用來(lái)表達(dá)對(duì)字符串的一種過(guò)濾邏輯。

2. 目的

給定一個(gè)正則表達(dá)式和另一個(gè)字符串,我們可以達(dá)到如下的目的:

  • a. 給定的字符串是否符合正則表達(dá)式的過(guò)濾邏輯(稱作“匹配”);

    例如:郵箱匹配,電話號(hào)碼匹配

  • b. 可以通過(guò)正則表達(dá)式,從字符串中獲取我們想要的特定部分。

    爬蟲中解析 HTML 數(shù)據(jù)

3. 特點(diǎn):

  • a. 靈活性、邏輯性和功能性非常的強(qiáng);
  • b. 可以迅速地用極簡(jiǎn)單的方式達(dá)到字符串的復(fù)雜控制。
  • c. 對(duì)于剛接觸的人來(lái)說(shuō),比較晦澀難懂。

4. 學(xué)習(xí)方法

  • a. 做好筆記,不要死記硬背
  • b. 大量練習(xí)

python 中通過(guò)系統(tǒng)庫(kù) re 實(shí)現(xiàn)正則表達(dá)式的所有功能

二、正則表達(dá)式符號(hào)

1. 普通字符

下面的案例使用 re 模塊的 findall() 函數(shù),函數(shù)參考如下:

  • re.findall(pattern, string, flag)
    
    • 在字符串中找到正則表達(dá)式所匹配的所有子串,并返回列表,如果沒有找到返回空列表
    • pattern: 正則表達(dá)式
    • string:被匹配的字符串
    • flag:標(biāo)志位用來(lái)控制正則表達(dá)式匹配方式

在最簡(jiǎn)單的情況下,一個(gè)正則表達(dá)式看上去就是一個(gè)普通的查找串

  import re
s1 = "testing123"
s2 = "Testing123"
r = re.findall("test", s1)  # 表示在s1中找到字符串"test"
print(r)  

運(yùn)行結(jié)果:

  ['test']
  r = re.findall("test", s2)
print(r)  

運(yùn)行結(jié)果:

  []
  r = re.findall("test", s2, re.I) # 修飾符re.I:使匹配對(duì)大小寫不敏感
print(r)  

運(yùn)行結(jié)果:

  ['Test']

2. 元字符

. ^ $ * + ? { } [ ] | ( ) \

2.1 通配符 .

匹配除 \n 之外的任何單個(gè)字符

  s1 = "testing123"
s2 = "testing123\n"
r = re.findall(".", s1)
print(r)

運(yùn)行結(jié)果:

  ['t', 'e', 's', 't', 'i', 'n', 'g', '1', '2', '3']
  r = re.findall(".", s2)  # 除“\n”
print(r)

運(yùn)行結(jié)果:

  ['t', 'e', 's', 't', 'i', 'n', 'g', '1', '2', '3']

修飾符 re.S 使 . 匹配包括換行在內(nèi)的所有字符

  r = re.findall(".", s2, re.S)  
print(r)

運(yùn)行結(jié)果:

  ['t', 'e', 's', 't', 'i', 'n', 'g', '1', '2', '3', '\n']

2.2 脫字符 ^

匹配輸入字符串的開始位置

  s1 = "testing\nTesting\ntest"

r = re.findall("^test", s1)  # 默認(rèn)只匹配單行
print(r)

運(yùn)行結(jié)果:

  ['test']
  r = re.findall("^test", s1, re.M)   # 修飾符re.M:多行匹配
print(r)  

運(yùn)行結(jié)果:

  ['test', 'test']
  r = re.findall("^test", s1, re.I | re.M)  
print(r)  # 輸出['test', 'Test', 'test']

運(yùn)行結(jié)果:

  ['test', 'Test', 'test']

2.3 美元符 $

匹配輸入字符串的結(jié)束位置

  s1 = "testing\nTesting\ntest"

r = re.findall("testing$", s1)  # 默認(rèn)匹配單行
print(r)  

運(yùn)行結(jié)果:

  []
  r = re.findall("testing$", s1, re.M)   # 修飾符re.M:多行匹配
print(r)  # 輸出['testing']

運(yùn)行結(jié)果:

  ['testing']
  r = re.findall("testing$", s1, re.I | re.M)    # 多個(gè)修飾符通過(guò) OR(|) 來(lái)指定
print(r)  # 輸出['testing', 'Testing']

運(yùn)行結(jié)果:

  ['testing', 'Testing']

2.4 重復(fù)元字符 *,+,?

  • * 匹配前面的子表達(dá)式任意次
  • + 匹配前面的子表達(dá)式一次或多次(至少一次)
  • ? 匹配前面的子表達(dá)式 0 次或 1 次
  s1 = "z\nzo\nzoo"

r = re.findall("zo*", s1)     # 匹配o{0,}
print(r)  

運(yùn)行結(jié)果:

  ['z', 'zo', 'zoo']
  r = re.findall("zo+", s1)   # 匹配o{1,}
print(r)  

運(yùn)行結(jié)果:

  ['zo', 'zoo']
  r = re.findall("zo?", s1)    # 匹配o{0,1}
print(r)  

運(yùn)行結(jié)果:

  ['z', 'zo', 'zo']

2.5 重復(fù)元字符 {}

也是控制匹配前面的子表達(dá)式次數(shù)

  s1 = "z\nzo\nzoo"
r = re.findall("zo*", s1)     # 匹配o{0,},逗號(hào)后不能空格
r1 = re.findall(r"zo{0,}", s1)
print(r)  # ['z', 'zo', 'zoo']
print(r1)  # ['z', 'zo', 'zoo']

運(yùn)行結(jié)果:

  ['z', 'zo', 'zoo']
['z', 'zo', 'zoo']
  r = re.findall("zo+", s1)   # 匹配o{1,}
r1 = re.findall(r"zo{1,}", s1)
print(r)  # 輸出['zo', 'zoo']
print(r1)  # 輸出['zo', 'zoo']

運(yùn)行結(jié)果:

  ['zo', 'zoo']
['zo', 'zoo']
  r1 = re.findall("zo{2}", s1)    # 匹配o{0,1}
print(r1)  # 輸出['zoo']

運(yùn)行結(jié)果:

  ['zoo']

2. 6 字符組 []

表示匹配給出的任意字符

  s1 = "test\nTesting\nzoo"

r = re.findall("[eio]", s1)   # 匹配包含的任意字符
print(r)  

運(yùn)行結(jié)果:

  ['e', 'e', 'i', 'o', 'o']
  r = re.findall("[e-o]", s1)   # 匹配包含的字符范圍
print(r)  

運(yùn)行結(jié)果:

  ['e', 'e', 'i', 'n', 'g', 'o', 'o']
  r = re.findall("^[eio]", s1, re.M)   # 回憶脫字符,匹配以[eio]開頭字符。
print(r)  

運(yùn)行結(jié)果:

  []
  r = re.findall("[^eio]", s1)    # 匹配未包含的任意字符
print(r) 

運(yùn)行結(jié)果:

  ['t', 's', 't', '\n', 'T', 's', 't', 'n', 'g', '\n', 'z']
  r1 = re.findall("[^e-o]", s1)    # 匹配未包含的字符范圍
print(r1)  # 輸出['t', 's', 't', '\n', 'T', 's', 't', '\n', 'z']
  ['t', 's', 't', '\n', 'T', 's', 't', '\n', 'z']

2.7 選擇元字符 |

表示兩個(gè)表達(dá)式選擇一個(gè)匹配

  s1 = "z\nzood\nfood"
r = re.findall("z|food", s1)   # 匹配"z"或"food"
print(r) 

運(yùn)行結(jié)果:

  ['z', 'z', 'food']
  r = re.findall("[z|f]ood", s1)   # 匹配"zood"或"food"
print(r)  # 

運(yùn)行結(jié)果:

  ['zood', 'food']

2.8 分組元字符 ()

將括號(hào)之間的表達(dá)式定義為組(group),并且將匹配這個(gè)子表達(dá)式的字符返回

  s1 = "z\nzood\nfood"

r = re.findall("[z|f]o*", s1)   # 不加分組,拿到的引號(hào)內(nèi)正則表達(dá)式匹配到的字符
print(r)

運(yùn)行結(jié)果:

  ['z', 'zoo', 'foo']
  r = re.findall("[z|f](o*)", s1)   # 加上分組,返回的將是引號(hào)內(nèi)正則表達(dá)式匹配到的字符中()中的內(nèi)容。
print(r)  # ['', 'oo', 'oo']

運(yùn)行結(jié)果:

  ['', 'oo', 'oo']

2.9 轉(zhuǎn)義元字符 \

用來(lái)匹配元字符本身時(shí)的轉(zhuǎn)義,和特定字符組成字符串,見預(yù)定義字符組

  s = '12345@qq.com'
r = re.findall('\.', s)
print(r)

運(yùn)行結(jié)果:

  ['.']

2.10 非貪婪模式

在默認(rèn)情況下,元字符 *,+{n,m} 會(huì)盡可能多的匹配前面的子表達(dá)式,這叫貪婪模式。

  s = "abcadcaec"

r = re.findall(r"ab.*c", s)   # 貪婪模式,盡可能多的匹配字符(.*或者.+)
print(r) 

運(yùn)行結(jié)果:

  ['abcadcaec']
  r = re.findall(r"ab.*?c", s)  # 非貪婪模式,盡可能少的匹配字符
print(r)  

運(yùn)行結(jié)果:

  ['abc']
  r = re.findall('ab.+c', s)  # 貪婪
print(r) 

運(yùn)行結(jié)果:

  ['abcadcaec']
  r = re.findall('ab.+?c', s)  # 非貪婪
print(r)

運(yùn)行結(jié)果:

  ['abcadc']
  r = re.findall('ab.{0,}', s) # 貪婪
print(r)

運(yùn)行結(jié)果:

  ['abcadcaec']
  r = re.findall('ab.{0,}?', s) # 非貪婪
print(r)

運(yùn)行結(jié)果:

  ['ab']

3. 預(yù)定義字符組

元字符 \ 與某些字符組合在一起表示特定的匹配含義

3.1 \d

匹配單個(gè)數(shù)字,等價(jià)于[0-9]

  s = "<a href=' asdf'>1360942725</a>"
a = re.findall('\d', s)
print(a) 

運(yùn)行結(jié)果:

  ['1', '3', '6', '0', '9', '4', '2', '7', '2', '5']
  a = re.findall('\d+', s)
print(a)  

運(yùn)行結(jié)果:

  ['1360942725']

3.2 \D

匹配任意單個(gè)非數(shù)字字符,等價(jià)于[^0-9]

  a = re.findall('\D', s)
print(a)

運(yùn)行結(jié)果:

  ['<', 'a', ' ', 'h', 'r', 'e', 'f', '=', "'", ' ', 'a', 's', 'd', 'f', "'", '>', '<', '/', 'a', '>']

3.3 \s

匹配任意單個(gè)空白符,包括空格,制表符(tab),換行符等

  s = 'fdfa**68687+ 我怕n fdg\tf_d\n'
a = re.findall('\s', s)
print(a)  

運(yùn)行結(jié)果:

  [' ', ' ', '\t', '\n']

3.4 \S

匹配任何非空白字符

  s = 'fdfa**68687+ 我怕n fdg\tf_d\n'
a = re.findall('\S', s)
print(a) 

運(yùn)行結(jié)果:

  ['f', 'd', 'f', 'a', '', '', '6', '8', '6', '8', '7', '+', '我', '怕', 'n', 'f', 'd', 'g', 'f', '_', 'd']

3.5 \w

匹配除符號(hào)外的單個(gè)字母,數(shù)字,下劃線或漢字等

  a = re.findall("\w", s)
print(a)

運(yùn)行結(jié)果:

  ['f', 'd', 'f', 'a', '6', '8', '6', '8', '7', '我', '怕', 'n', 'f', 'd', 'g', 'f', '_', 'd']

小案例

  1. 檢測(cè)郵箱
  s = "3003756995@qq.com"
a = re.findall('^\w+@\w+\.com$', s) # 檢測(cè)郵箱
if a:
    print('是正確格式的郵箱')
else:
    print('不是郵箱地址')

是正確格式的郵箱
\2. 檢測(cè)手機(jī)號(hào)碼

  s = '13812345678'
r = re.findall('^1[3-9]\d{9}$', s)  # 檢查手機(jī)號(hào)碼
if r:
    print('手機(jī)號(hào)碼格式正確')
else:
    print('手機(jī)號(hào)碼格式不正確')
  

手機(jī)號(hào)碼格式正確

4.re 模塊常用函數(shù)

4.1 re.match

  • re.match(pattern, string, flag)
    
    • 嘗試從字符串的起始位置匹配一個(gè)模式,成功返回匹配對(duì)象,否則返回 None
    • pattern: 正則表達(dá)式
    • string: 被匹配的字符串
    • flag: 標(biāo)志位,表示匹配模式
  import re
url = 'www.hhxpython.com'
res = re.match('www', url)    # 'www' 就是正則表達(dá)式,沒有元字符表示匹配字符本身
                              # re.match默認(rèn)是從字符串開頭匹配,等價(jià)于'^www'
print(res)

運(yùn)行結(jié)果:

  <re.Match object; span=(0, 3), match='www'>
  res2 = re.match('hhx', url)
print(res2)

運(yùn)行結(jié)果:

  None
匹配對(duì)象

match 函數(shù)返回一個(gè)匹配對(duì)象,通過(guò)這個(gè)對(duì)象可以取出匹配到的字符串和分組字符串

  line = 'Good good study, Day day up!'
match_obj = re.match('(?P<aa>.*), (.*) (.*)', line)  
if match_obj: 
    print(match_obj.group())    # 返回匹配到的字符串
    print(match_obj.group(1))   # 返回對(duì)應(yīng)序號(hào)分組字符串 從1開始
    print(match_obj.group(2))
    print(match_obj.group(3))
else:
    print('not found')
print(match_obj.groups())  # 返回分組字符串元組
print(match_obj.groupdict())  # 按照分組名和分組字符串組成字典 (?P<name>pattern)

運(yùn)行結(jié)果:

  Good good study, Day day up!
Good good study
Day day
up!
('Good good study', 'Day day', 'up!')
{'aa': 'Good good study'}

4.2 re.search

  • re.search(pattern, string, flag)
    
    • 掃描整個(gè)字符串返回第一個(gè)成功的匹配對(duì)象
    • pattern: 正則表達(dá)式
    • string: 被匹配的字符串
    • flag: 標(biāo)志位,表示匹配模式
  url = 'www.hhxpython.com'
res = re.search('www', url)    # 'www' 就是正則表達(dá)式,沒有元字符表示匹配字符本身
print(res)

運(yùn)行結(jié)果:

  <re.Match object; span=(0, 3), match='www'>
  res2 = re.search('hhx', url)
print(res2)

運(yùn)行結(jié)果:

  <re.Match object; span=(4, 7), match='hhx'>
  res3 = re.search('h', url)
print(res3)

運(yùn)行結(jié)果:

  <re.Match object; span=(4, 5), match='h'>

4.3 re.sub

  • re.sub(pattern, repl, string, count=0, flag)
    
    • 將表達(dá)式匹配到的部分替換為制定字符串,返回替換后的新字符串
    • pattern: 正則表達(dá)式
    • repl: 用來(lái)替換的字符串
    • string: 被匹配的字符串
    • count: 替換次數(shù),默認(rèn)為 0,表示全部替換
    • flags: 標(biāo)志位,表示匹配模式
  phone = '2004-959-559 # 這是一個(gè)國(guó)外電話號(hào)碼'

# 刪除字符串中的python注釋
num = re.sub('#.*', '', phone)
print(num)

運(yùn)行結(jié)果:

  2004-959-559
  # 刪除連接符號(hào) -
num = re.sub('-', '', num)
print(num)

運(yùn)行結(jié)果:

  2004959559

4.4 re.findall

  • re.findall(pattern, string, flags=0)
    
    • 在字符串中找到正則表達(dá)式匹配的所有子串,返回一個(gè)列表,匹配失敗則返回空列表
    • pattern: 正則表達(dá)式
    • string: 被匹配的字符串
    • flags: 標(biāo)志位,表示匹配模式
  res1 = re.findall('day', line, re.I)
res2 = re.search('day', line, re.I)
res3 = re.match('day', line, re.I)

print('findall', res1)
print('search', res2.group())
print('search', res3)

運(yùn)行結(jié)果:

  findall ['Day', 'day']
search Day
search None
match,search,findall 的區(qū)別
  • match 從頭開始匹配,成功返回匹配對(duì)象,失敗返回 None
  • search 只匹配第一個(gè),成功返回匹配對(duì)象,失敗返回 None
  • findall 匹配所有,成功返回所有匹配到的字符串組成的列表,失敗返回空列表

4.5 re.compile

  • re.compile(pattern, [flags])
    
    • compile 函數(shù)用于編譯正則表達(dá)式,生成一個(gè)正則表達(dá)式對(duì)象,該對(duì)象調(diào)用 findall,search,match,sub 等方法
    • pattern: 正則表達(dá)式
    • flags: 標(biāo)志位,表示匹配模式
      面向?qū)ο缶幊虝r(shí)使用
  pattern = re.compile('day', re.I)
res1 = pattern.findall(line)
res2 = pattern.search(line)
res3 = pattern.match(line)
print('findall', res1)
print('search', res2.group())
print('match', res3)

運(yùn)行結(jié)果:

  findall ['Day', 'day']
search Day
match None

三、應(yīng)用場(chǎng)景

在做自動(dòng)化測(cè)試時(shí)用例數(shù)據(jù)是動(dòng)態(tài)的,需要程序自動(dòng)處理。

  import re
# 用例json數(shù)據(jù)字符串
data = '{"member_id":"#member_id#","pwd":"#pwd#","user":"#user#","loan_id":"#loan#"}'
# 動(dòng)態(tài)數(shù)據(jù)
class DynamicData:
    member_id = 123
    user = "musen"
    pwd = "lemonban"
    loan = 31

def mk_data(json_str,dn_data):
    while True:
        item = re.search('#(.*?)#', json_str)
        # 沒有匹配到說(shuō)明匹配完成
        if not item:
            break
        # 需要被替換的字符串
        need_replace = item.group()
        # 需要被替換的參數(shù)名
        key = item.group(1)
        # 替換
        json_str = json_str.replace(need_replace, str(getattr(dn_data, key)))
    return json_str

mk_data(data, DynamicData)

運(yùn)行結(jié)果:

  '{"member_id":"123","pwd":"lemonban","user":"musen","loan_id":"31"}'
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容