引言
文本處理已經成為計算機常見工作之一, 對文本的搜索、定位、提取的邏輯往往比較復雜, 為了解決上述問題,產生正則表達式技術
正則表達式即文本的高級匹配模式,提供搜索,替代,獲取等功能。本質是由一系列特殊符號和字符構成的自串,這個自串就是正則表達式。
正則表達式能夠方便地進行檢索和修改等文本操作;靈活多樣;支持語言眾多;
正則表達式匹配手段:通過設定有特殊意義的符號,描述符號和字符的重復行為及位置特征來表示一類特定規則的字符串
import re
re.findall(pattern, string)
參數:
- pattern: 以字符串的形式傳入一個正則表達式
- string:要匹配的目標字符串
返回值:
- 得到一個列表,將目標子串中能用正則表達式匹配的內容放入列表
元字符的使用
普通字符
匹配規則:每個普通字符匹配其對應的字符
re.findall('ab',"abcdefabcd") # ['ab', 'ab']
或
元字符: |
匹配規則:匹配 | 兩側的正則表達式
re.findall('com|cn',"www.baidu.com/www.tmooc.cn") # ['com', 'cn']
匹配單個字符
元字符: .
匹配規則:匹配除 \n外任意一個字符
re.findall('張.豐',"張三豐,張四豐,張五豐") # ['張三豐', '張四豐', '張五豐']
匹配字符集
元字符: [字符集]
匹配規則: 匹配字符集中的任意一個字符
表達形式:
- [abc#!好] 表示 [ ] 中的任意一個字符
- [0-9], [a-z], [A-Z] 表示區間內的任意一個字符
- [_#?0-9a-z] 混合書寫,一般區間表達寫在后面
re.findall('[aeiou]',"How are you!") # ['o', 'a', 'e', 'o', 'u']
匹配字符集反集
元字符: [^字符集]
匹配規則: 匹配除了字符集以外的任意一個字符
re.findall('[^0-9]',"Use 007 port") # ['U', 's', 'e', ' ', ' ', 'p', 'o', 'r', 't']
匹配開始位置
- 元字符: ^
- 匹配規則: 匹配目標字符串的開頭位置
re.findall('^Jame',"Jame,hello") # ['Jame']
匹配結束位置
- 元字符:$
- 匹配規則:匹配目標字符串的結束位置
re.findall('Jame$',"Hi,Jame") # ['Jame']
- 規則技巧: ^ 和 $必然出現在正則表達式的開頭和結尾處。如果兩者同時出現,則中間的部分必須匹配整個目標字符串的全部內容。
匹配字符重復
- 元字符: *
- 匹配規則: 匹配前面的字符出現0次或多次
re.findall("wo*", "woooo~~w!") # w后面o出現多次的情況和出現0次的情況 # ['woooo', 'w']
# 大寫字母后面跟小寫之母出現0次或多次 re.findall("[A-Z][a-z]*", "How ary you? Finf Ha") # ['How', 'Finf', 'Ha']
- 元字符: +
- 匹配規則: 匹配前面的字符出現后面的字符的次數1次或多次的情況
# 大寫之母后面跟小寫之母出現1次或多次 re.findall('[A-Z][a-z]+',"Hello World") # ['Hello', 'World']
- 元字符: ?
- 匹配規則: 匹配前面的字符出現0次或1次
# 匹配整數 re.findall('-?[0-9]+',"Jame,age:18, -26") # ['18', '-26']
- 元字符: {n}
- 匹配規則: 匹配前面的字符出現n次
# 匹配手機號碼 re.findall('1[0-9]{10}',"Jame:13886495728") # ['13886495728']
- 元字符: {m,n}
- 匹配規則: 匹配前面的字符出現 [m-n] 次
# 匹配qq號 re.findall('[1-9][0-9]{5,10}',"Baron:1259296994") # ['1259296994']
匹配任意(非)數字字符
- 元字符: \d \D
- 匹配規則: \d 匹配任意數字字符, \D 匹配任意非數字字符
# 匹配端口 re.findall('\d{1,5}',"Mysql: 3306, http:80") # ['3306', '80']
匹配任意(非)普通字符
- 元字符: \w \W
- 匹配規則: \w 匹配普通字符, \W 匹配非普通字符
- 說明: 普通字符指 數字, 字母, 下划線, 漢字。
re.findall('\w+',"server_port = 8888") # ['server_port', '8888']
匹配任意(非)空字符
- 元字符: \s \S
- 匹配規則: \s 匹配空字符, \S 匹配非空字符
- 說明:空字符指 空格 \r \n \t \v \f 字符
re.findall('\w+\s+\w+',"hello world") # ['hello world']
匹配開頭結尾位置
- 元字符: \A \Z
- 匹配規則: \A 表示開頭位置, \Z 表示結尾位置
re.findall("\Ahello","hello world") # ['hello'] re.findall("world\Z","hello world") # ['world']
匹配(非)單詞的邊界位置
- 元字符: \b \B
- 匹配規則: \b 表示單詞邊界, \B 表示非單詞邊界
- 說明:單詞邊界指數字字母(漢字)下划線與其他字符的交界位置。
re.findall(r'\bis\b',"This is a test.") # ['is']
總結:
| 類別 | 元字符 |
| 匹配字符 | . [...] [^...] \d \D \w \W \s \S |
| 匹配重復 | * + ? {n} {m,n} |
| 匹配位置 | ^ $ \A \Z \b \B |
| 其他 | | () \ |
正則表達式的轉義
1. 如果使用正則表達式匹配特殊字符則需要加 \ 表示轉義。
特殊字符: . * + ? ^ $ [] () {} | \
# 匹配特殊字符 . 時使用 \. 表示本身含義 re.findall('-?\d+\.?\d*',"123,-123,1.23,-1.23") # ['123', '-123', '1.23', '-1.23']
2. 在編程語言中,常使用原生字符串書寫正則表達式避免多重轉義的麻煩。
python字符串 --> 正則 --> 目標字符串
"\\$\\d+" 解析為 \$\d+ 匹配 "$100"
"\\$\\d+" 等同於 r"\$\d+"
貪婪模式和非貪婪模式
貪婪模式: 默認情況下,匹配重復的元字符總是盡可能多的向后匹配內容。比如: * + ? {m,n}
非貪婪模式(懶惰模式): 讓匹配重復的元字符盡可能少的向后匹配內容。
貪婪模式轉換為非貪婪模式: 在匹配重復元字符后加 '?' 號即可
* ---> *? ; + ---> +? ; ? ---> ?? {m,n} ---> {m,n}?
re.findall("ab*", "abbbbb") # ['abbbbb'] re.findall("ab*?", "abbbbb") # ['a'] re.findall("ab+", "abbbbb") # ['abbbbb'] re.findall("ab+?", "abbbbb") # ['ab']
練習"
# 把數字匹配出來 re.findall("[^ ]+", "12 -36 28 1.34 -3.8") # ['12', '-36', '28', '1.34', '-3.8'] re.findall("-?\d+\.?\d*", "12 -36 28 1.34 -3.8") # ['12', '-36', '28', '1.34', '-3.8']
# 匹配一個.com郵箱格式字符串 print(re.findall(r"\w+@\w+", "lvze@163.com")) # 匹配一個密碼 8-12位數字字母下划線構成 print(re.findall(r"\w{8,12}", "Tedu023256")) # 匹配一個數字 正數,負數,整數,小數,分數1/2,百分數45% print(re.findall(r"-?\d+/?\.?\d*%?", "12 -3 3.5 5.45 42% 1/3")) # 匹配一段文字中以大寫字母開頭的單詞,注意文字中可能有ipython(不算)H-base(算),單詞可能有大寫字母小寫之母 -_ print(re.findall(r"\b[A-Z][-_a-zA-Z]*", "Hello ipython H-base BSD"))
分組
在正則表達式中, 以 () 建立正則表達式的內部分組, 匹配括號內的表達式,分組的作用是在完整的模式中定義子模型,將每個圓括號中子模式專門匹配出來。
- 可以被作為整體操作, 改變元字符的操作對象
# 改變 +號 重復的對象 re.search(r'(ab)+',"ababababab").group() # 'ababababab' # 改變 |號 操作對象 re.search(r'(王|李)\w{1,3}',"王者榮耀").group() # '王者榮耀'
- 可以通過編程語言某些接口獲取匹配內容中,子組對應的內容部分
# 獲取url協議類型 re.search(r'(https|http|ftp|file)://\S+',"https://www.baidu.com").group(1) # 'https'
先匹配外部正則,再進一步匹配括號中的正則
import re s = 'A B C D' p1 = re.compile('\w+\s+\w+') print(p1.findall(s)) # # ['A B','C D'] p2 = re.compile('(\w+)\s+\w+') print(p2.findall(s)) # # ['A','C'] p3 = re.compile('(\w+)\s+(\w+)') print(p3.findall(s)) # # [('A','B'),('C','D')]
import re html = '''<div class="animal"> <p class="name"> <a title="Tiger"></a> </p> <p class="content"> Two tigers two tigers run fast </p> </div> <div class="animal"> <p class="name"> <a title="Rabbit"></a> </p> <p class="content"> Small white rabbit white and white </p> </div>''' pattern = re.compile( '<div class="animal">.*?title="(.*?)".*?' 'class="content">(.*?)</p>', re.S) r_list = pattern.findall(html) print(r_list)
分組總結
- 在網頁中,想要什么內容,就加 ( )
- 先按整體正則匹配,然后再提取分組()中的內容
- 如果有2個及以上分組(),則結果中以元組形式顯示 [(),(),()]
捕獲組
可以給正則表達式的子組起一個名字,表達該子組的意義。這種有名稱的子組即為捕獲組。
格式: (?P<name>pattern)
# 給子組命名為 "pig" re.search(r'(?P<pig>ab)+',"ababababab").group('pig') # 'ab'
注意事項
- 一個正則表達式中可以包含多個子組
- 子組可以嵌套,但是不要重疊或者嵌套結構復雜
- 子組序列號一般從外到內,從左到右計數
Python re模塊
re.match函數
re.match(pattern, string, flags=0)
參數:
- pattern 正則
- string 目標字符串
- flags 標志位, 用於控制表達式的匹配方式,如:是否區分大小寫...
返回值:匹配內容match 對象
re.match嘗試從字符串的起始位置匹配一個模型,如果不是起始位置匹配成功的話,match()就返回none.
import re print(re.match('www', 'www.runoob.com').span()) # 在起始位置匹配, (0, 3) print(re.match('com', 'www.runoob.com')) # 不在起始位置匹配, None
我們可以使用group()或groups()匹配對象函數來匹配表達式.
- group(num=0) 匹配的整個表達式的字符串,group() 可以一次輸入多個組號,在這種情況下它將返回一個包含那些組所對應值的元組。
- groups() 返回一個包含所有小組字符串的元組,從 1 到 所含的小組號。
import re line = "Cats are smarter than dogs" # .* 表示任意匹配除換行符(\n、\r)之外的任何單個或多個字符 matchObj = re.match(r'(.*) are (.*?) .*', line) print(matchObj.group()) # Cats are smarter than dogs print(matchObj.group(1)) # Cats print(matchObj.group(2)) # smarter m = re.match(r"[A-Z]\w*", "Hello World") print(m.group()) # Hello
re.search函數
re.search 掃描整個字符串並返回第一個成功的匹配。
re.search(pattern, string, flags=0)
參數:
- pattern 正則
- string 目標字符串
- flags 標志位, 用於控制表達式的匹配方式,如:是否區分大小寫...
返回值: 匹配目標字符串第一個符合內容 的對象
我們可以使用group(num) 或 groups() 匹配對象函數來獲取匹配表達式。同re.match
import re line = "Cats are smarter than dogs" # .* 表示任意匹配除換行符(\n、\r)之外的任何單個或多個字符 searchObj = re.search(r'(.*) are (.*?) .*', line) print(searchObj) # <_sre.SRE_Match object; span=(0, 26), match='Cats are smarter than dogs'> print(searchObj.group()) # Cats are smarter than dogs print(searchObj.group(1)) # Cats print(searchObj.group(2)) # smarter # 匹配第一處 m = re.search(r"[A-Z]\w*", " Hello World") print(m.group()) # Hello
match和search區別: re.match只匹配字符串的開始,如果字符串開始不符合正則表達式,則匹配失敗,函數返回None;而re.search匹配整個字符串,直到找到一個匹配。
re.sub()替換函數
re.sub(pattern,replace,string,max,flags = 0)
使用一個字符串替換正則表達式匹配到的內容
參數:
- pattern 正則表達式
- replace 替換的字符串
- string 目標字符串
- max 最多替換幾處, 默認替換全部
- flags 功能標志位,擴展正則表達式的匹配
返回值: 替換后的字符串
import re phone = "2004-959-559 # 這是一個電話號碼" # 刪除注釋 num_1 = re.sub(r'#.*$', "", phone) # 2004-959-559 # 移除非數字的內容 num_2 = re.sub(r'\D', "", phone) # 2004959559 # 替換匹配到的內容 s = re.subn(r'\s+', '#', "This is a test", 2) # ('This#is#a test', 2)
compile 函數
regex = compile(pattern,flags = 0)
參數:
- pattern 正則表達式
- flags 功能標志位,擴展正則表達式的匹配
返回值: 正則表達式對象
compile 函數用於編譯正則表達式,生成一個正則表達式( Pattern )對象,供 match() 和 search() 這兩個函數使用。
import re pattern = re.compile(r'([a-z]+) ([a-z]+)', re.I) # re.I 表示忽略大小寫 m = pattern.match('Hello World Wide Web') print(m) # 匹配成功,返回一個 Match 對象 # <_sre.SRE_Match object; span=(0, 11), match='Hello World'> print(m.group(0)) # 返回匹配成功的整個子串,Hello World print(m.group(1)) # 返回第一個分組匹配成功的子串,Hello print(m.groups()) # 等價於 (m.group(1), m.group(2), ...) ('Hello', 'World') print(m.group(3)) # 不存在第三個分組 報錯!!! print(m.span(0)) # 返回匹配成功的整個子串的索引,(0, 11)
re.findall()函數
re.findall(pattern,string,flags = 0)
參數:
- pattern 正則表達式
- string 目標字符串
- flags 功能標志位,擴展正則表達式的匹配
返回值: 在字符串中找到正則表達式所匹配的所有子串,並返回一個列表,如果沒有找到匹配的,則返回空列表。
注意: match 和 search 是匹配一次 findall 匹配所有。
import re # 目標字符串 s = "Alex:1994,Sunny:1993" pattern = r"(\w+):(\d+)" # re模塊調用findall l = re.findall(pattern, s) print(l) # [('Alex', '1994'), ('Sunny', '1993')]
re.finditer(pattern,string,flags = 0) 根據正則表達式匹配目標字符串內容
參數:
- pattern 正則表達式
- string 目標字符串
- flags 功能標志位,擴展正則表達式的匹配
返回值: 匹配結果的迭代器, 返回的對象要通過.group()取值
import re s = "2019年,建國70年" pattern = r'\d+' # 返回迭代器 it = re.finditer(pattern,s) for i in it: print(i) # 獲取match對象對應內容 # <_sre.SRE_Match object; span=(0, 4), match='2019'> # <_sre.SRE_Match object; span=(0, 4), match='70'>
re.fullmatch(pattern,string,flags=0)
參數:
- pattern 正則
- string 目標字符串
返回值:完全匹配某個目標字符串 object
# 完全匹配 print(re.fullmatch("[,\w]+", s).group()) # 2019年,建國70年
re.split(pattern,string,flags = 0) 使用正則表達式匹配內容,切割目標字符串
參數:
- pattern 正則表達式
- string 目標字符串
- flags 功能標志位,擴展正則表達式的匹配
返回值: 切割后的內容列表
s = "Alex:1994,Sunny:1993" # 按照匹配內容切割字符串 l = re.split(r'[:,]', s) print(l) # ['Alex', '1994', 'Sunny', '1993']
