1. 程式人生 > >day34-常見內建模組三(re模組)

day34-常見內建模組三(re模組)

re模組

1、什麼是正則
  正則就是用一些具有特殊含義的符號組合到一起(稱為正則表示式)來描述字元或者字串的方法。或者說:正則就是用來描述一類事物的規則。(在Python中)它內嵌在Python中,並通過re模組實現。正則表示式模式被編譯成一系列的位元組碼,然後由用C編寫的匹配引擎執行。

元字元   匹配內容
\w     匹配字母(包含中文)或數字或下劃線
\W    匹配非字母(包含中文)或數字或下劃線
\s     匹配任意的空白符
\S     匹配任意非空白符
\d     匹配數字
\D     匹配非數字
\A     從字串開頭匹配
\Z     匹配字串的結束
\n     匹配一個換行符
\t      匹配一個製表符
^      匹配字串的開始
$      匹配字串的結尾
.       匹配任意字元,除了換行符,當re.DOTALL標記被指定時,則可以匹配包括換行符的任意字元。
[...]      匹配字元組中的字元
[^...]       匹配除了字元組中的字元的所有字元
*       匹配0個或者多個左邊的字元。
+         匹配一個或者多個左邊的字元。
?        匹配0個或者1個左邊的字元,非貪婪方式。
{n}       精準匹配n個前面的表示式。
{n,m}     匹配n到m次由前面的正則表示式定義的片段,貪婪方式
a|b      匹配a或者b。
()         匹配括號內的表示式,也表示一個組

 

2、匹配模式舉例
之前學過的字串的常用操作:一對一匹配

s1 = 'abcdefg你好'
print(s1.find('你好'))
# 7 返回的是字串開始的下標

2.1、單個匹配:

import re
1)、\w 和 \W 漢字字母數字下劃線
print(re.findall('\w', '你好[email protected]#$%^&*()-_=+'))
# ['你', '好', 'T', 'o', 'm', '1', '2', '3', '_']
print(re.findall('\W', '你好[email protected]
#$%^&*()-_=+
')) # ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '=', '+'] 2)、 \s 和 \S 空格、TAB、換行符、回車 print(re.findall('\s','a 1\t\n\r')) # [' ', '\t', '\n', '\r'] print(re.findall('\S','a 1\t\n\r')) # ['a', '1'] 3)、\d 和 \D 數字 print(re.findall('\d','你好[email protected]#$%^&*()-_=+
')) # ['1', '2', '3'] print(re.findall('\D','你好[email protected]#$%^&*()-_=+')) # ['你', '好', 'T', 'o', 'm', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-', '_', '=', '+'] 4)、\A 和 ^ 以什麼開頭,字串寫在後面 print(re.findall('\Ahel','[email protected]#')) # ['hel'] print(re.findall('^hello','[email protected]#')) # ['hello'] 5)、\Z 和 $ 以什麼結尾,字串寫在前面 print(re.findall('@#\Z','[email protected]#')) # ['@#'] print(re.findall('[email protected]#$','[email protected]#')) # ['[email protected]#'] 6)、\n 和 \t \r print(re.findall('\n','h e l \n \t \r 123')) # ['\n'] print(re.findall('\t','h e l \n \t \r 123')) # ['\t'] print(re.findall('\r','h e l \n \t \r 123')) # ['\r']

2.2、重複匹配 . ? * + {m,n} .* .*?

import re
# . 匹配任意字元,除了換行符(re.DOTALL這個引數可以匹配\n)
print(re.findall('a.b','aab a b a1b a*b a.b a\nb'))
# ['aab', 'a b', 'a1b', 'a*b', 'a.b']
print(re.findall('a.b','aab a b a1b a*b a.b a\nb',re.DOTALL))
# ['aab', 'a b', 'a1b', 'a*b', 'a.b', 'a\nb']

# ? 匹配0個或1個左邊字元
print(re.findall('a?b','aab aaab abb aabb'))
# ['ab', 'ab', 'ab', 'b', 'ab', 'b']

# * 匹配0個或多個左邊字元,滿足貪婪匹配
print(re.findall('a*b','aab aaab abb aabb'))
# ['aab', 'aaab', 'ab', 'b', 'aab', 'b']

# + 匹配1個或多個左邊字元,滿足貪婪匹配
print(re.findall('a+b','ab aab aaab abbb'))
# ['ab', 'aab', 'aaab', 'ab']

# {m,n}  匹配m個至n個左邊字元,滿足貪婪匹配
print(re.findall('a{2,4}b','ab aab aaab aaaab aaaaab'))
# ['aab', 'aaab', 'aaaab', 'aaaab']

# .* 匹配0個或多個任意非換行字元,貪婪匹配(從頭到尾匹配出來),加re.DOTALL就可以匹配換行符
print(re.findall('a.*b','ab aab a*()b a b a\nb'))
# ['ab aab a*()b a b']
print(re.findall('a.*b','ab aab a*()b a b a\nb',re.DOTALL))
# ['ab aab a*()b a b a\nb']


# .*? 匹配0個或多個任意非換行字元,非貪婪匹配(一個一個匹配出來),加re.DOTALL就可以匹配換行符
#這裡的?不是0個或1個的意思,而是表示.*模式的非貪婪匹配,re.DOTALL可以匹配換行
print(re.findall('a.*?b','ab aab a*()b aaaab a b a\nb'))
# ['ab', 'aab', 'a*()b', 'aaaab', 'a b']

# []: 括號中可以放任意一個字元,一箇中括號代表一個字元
# - 在[]中表示範圍,如果想要匹配上- 那麼這個-符號不能放在中間,只能放在兩邊
# ^ 在[]中表示取反的意思.

print(re.findall('a.b', 'a1b a3b aeb a*b arb a_b'))  # ['a1b', 'a3b', 'a4b', 'a*b', 'arb', 'a_b']
print(re.findall('a[abc]b', 'aab abb acb adb afb a_b'))  # ['aab', 'abb', 'acb']
print(re.findall('a[0-9]b', 'a1b a3b aeb a*b arb a_b'))  # ['a1b', 'a3b']
print(re.findall('a[a-z]b', 'a1b a3b aeb a*b arb a_b'))  # ['aeb', 'arb']
print(re.findall('a[a-zA-Z]b', 'aAb aWb aeb a*b arb a_b'))  # ['aAb', 'aWb', 'aeb', 'arb']
print(re.findall('a[0-9][0-9]b', 'a11b a12b a34b a*b arb a_b'))  # ['a11b', 'a12b', 'a34b']
print(re.findall('a[*-+]b','a-b a*b a+b a/b a6b'))  # ['a*b', 'a+b']

# - 在[]中表示範圍,如果想要匹配上- 那麼這個-符號不能放在中間.
print(re.findall('a[-*+]b','a-b a*b a+b a/b a6b'))  # ['a-b', 'a*b', 'a+b']
print(re.findall('a[^a-z]b', 'acb adb a3b a*b'))  # ['a3b', 'a*b']

2.3、分組:

() 制定一個規則,將滿足規則的結果匹配出來
找到字串中'Tom_123 Mike_123 Jack_123'的 Tom、Mike、Jack
print(re.findall('([a-zA-Z]+)_123','Tom_123 Mike_123 Jack_123'))

找出a標籤中的URL
print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">點選</a>'))#['http://www.baidu.com']

| 或匹配 
print(re.findall('Tom|Mike|Jack', ' Tom123MikeaabJack'))  # ['alex', '太白', 'wusir', '太白']
print(re.findall('compan(y|ies)','Too many companies have gone bankrupt, and the next one is my company'))  # ['ies', 'y']
print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company'))  # ['companies', 'company']
分組() 中加入?: 表示將整體匹配出來而不只是()裡面的內容。

 

3、常用方法舉例

3.1、findall 全部找到返回一個列表。
print(re.findall('a', 'alexwusirbarryeval'))  # ['a', 'a', 'a']

3.2、search 只到找到第一個匹配然後返回一個包含匹配資訊的物件,該物件可以通過呼叫group()方法得到匹配的字串,如果字串沒有匹配,則返回None。
print(re.search('Tom|Mike', 'Mike and Tom are 18 years old'))  # <_sre.SRE_Match object; span=(0, 4), match='Mike'>
print(re.search('Tom|Mike', 'Mike and Tom are 18 years old').group())  # Mike

3.3、match 同search,不過在字串開始處進行匹配,完全可以用search+^代替match
print(re.match('Tom|Mike', 'Mike and Tom are 18 years old')) # <_sre.SRE_Match object; span=(0, 4), match='Mike'>
print(re.match('Tom|Mike', 'Mike and Tom are 18 years old').group()) # Mike

如果開頭沒有找到,則返回None
print(re.match('Tom|Mike', '123 Mike and Tom are 18 years old')) #None
這時如果group()就會報錯

3.4、split 分割 可按照任意分割符進行分割
print(re.split('[ ::,;;,]','Tom:Mike:Jack,Rose;Bob;Lucy'))  #['Tom', 'Mike', 'Jack', 'Rose', 'Bob', 'Lucy']

3.5、sub 替換
print(re.sub('Tom', 'Mike', 'Tom is a student, Tom is 18 years old。')) # Mike is a student, Mike is 18 years old。

可以指定替換的次數
print(re.sub('Tom', 'Mike', 'Tom is a student, Tom is 18 years old。',1)) #Mike is a student, Tom is 18 years old。

也可以使用位置替換,將所有的字串按位置編號重新組合
print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)', r'\5\2\3\4\1\6\7\8\9', r'Tom and Mike are friends'))
# Mike and Tom are friends
print(re.sub('([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)([^a-zA-Z]+)([a-zA-Z]+)', r'\5\2\3\4\1', r'Tom and Mike are friends'))
# Mike and Tom

3.6、compile表示式
obj=re.compile('\d{2}')
print(obj.search('abc123eeee').group()) #12
print(obj.findall('abc123eeee')) #['12'],重用了obj

3.7、finditer 迭代器
ret = re.finditer('\d', 'ds3sy4784a')   #finditer返回一個存放匹配結果的迭代器
print(ret)  # <callable_iterator object at 0x10195f940>
print(next(ret).group())  #檢視第一個結果
print(next(ret).group())  #檢視第二個結果
print([i.group() for i in ret])  #檢視剩餘的結果

 

4、命名分組舉例(瞭解)

命名分組匹配:
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
還可以在分組中利用?<name>的形式給分組起名字
獲取的匹配結果可以直接用group('名字')拿到對應的值
print(ret.group('tag_name'))  #結果 :h1
print(ret.group())  #結果 :<h1>hello</h1>

ret = relx.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
如果不給組起名字,也可以用\序號來找到對應的組,表示要找的內容和前面的組內容一致
獲取的匹配結果可以直接用group(序號)拿到對應的值
print(ret.group(1))
print(ret.group())  #結果 :<h1>hello</h1>
View Code

 

5、相關小練習

1、1-2*(60+(-40.35/5)-(-4*3))"
  1.1 匹配所有的整數
print(re.findall('\d+',"1-2*(60+(-40.35/5)-(-4*3))"))
# ['1', '2', '60', '40', '35', '5', '4', '3']

  1.2 匹配所有的數字(包含小數)
print(re.findall(r'\d+\.?\d*|\d*\.?\d+', "1-2*(60+(-40.35/5)-(-4*3))"))
# ['1', '2', '60', '40.35', '5', '4', '3']

  1.3 匹配所有的數字(包含小數包含負號)
print(re.findall(r'-?\d+\.?\d*|\d*\.?\d+', "1-2*(60+(-40.35/5)-(-4*3))"))
['1', '-2', '60', '-40.35', '5', '-4', '3']


2、匹配一段你文字中的每行的郵箱
http://blog.csdn.net/make164492212/article/details/51656638 匹配所有郵箱
例項1、只允許英文字母、數字、下劃線、英文句號、以及中劃線組成
舉例:zhangsan-001@gmail.com 

分析郵件名稱部分:
26個大小寫英文字母表示為a-zA-Z
數字表示為0-9
下劃線表示為_
中劃線表示為-
由於名稱是由若干個字母、數字、下劃線和中劃線組成,所以需要用到+表示多次出現
根據以上條件得出郵件名稱表示式:[a-zA-Z0-9_-]+ 

分析域名部分:
一般域名的規律為“[N級域名][三級域名.]二級域名.頂級域名”,比如“qq.com”、“www.qq.com”、“mp.weixin.qq.com”、“12-34.com.cn”,分析可得域名類似“** .** .** .**”組成。
“**”部分可以表示為[a-zA-Z0-9_-]+
“.**”部分可以表示為\.[a-zA-Z0-9_-]+
多個“.**”可以表示為(\.[a-zA-Z0-9_-]+)+
綜上所述,域名部分可以表示為[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+

最終表示式: 
由於郵箱的基本格式為“名稱@域名”,需要使用“^”匹配郵箱的開始部分,用“$”匹配郵箱結束部分以保證郵箱前後不能有其他字元,所以最終郵箱的正則表示式為: 
^[a-zA-Z0-9_-][email protected][a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$

例項2、名稱允許漢字、字母、數字,域名只允許英文域名
舉例:楊元慶[email protected]

分析郵件名稱部分:
漢字在正則表示為[\u4e00-\u9fa5]
字母和數字表示為A-Za-z0-9 
通過分析得出郵件名稱部分表示式為[A-Za-z0-9\u4e00-\u9fa5]+
    
分析郵件域名部分
郵件部分可以參考例項1中的分析域名部分。 
得出域名部分的表示式為[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+。

最終表示式: 
我們用@符號將郵箱的名稱和域名拼接起來,因此完整的郵箱表示式為 
^[A-Za-z0-9\u4e00-\u9fa5][email protected][a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$
    
3、匹配一段你文字中的每行的時間字串 這樣的形式:'1995-04-27'
s1 = '''
時間就是1980-01-02,2008-03-04
1980-01-02 is Tom's birthday
Jack 1931-05-10:2010-10-20
2018-12-03
'''
print(re.findall('\d{4}-\d{2}-\d{2}', s1))
# ['1980-01-02', '2008-03-04', '1980-01-02', '1931-05-10', '2010-10-20', '2018-12-03']

4、匹配 一個浮點數
print(re.findall('\d+\.\d*','1.17'))
# ['1.17']

5、匹配qq號:騰訊從10000開始:
print(re.findall('[1-9][0-9]{4,}', '2413545136'))
# ['2413545136']

6、匹配標籤
s1 = '''
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7459977.html" target="_blank">python基礎一</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7562422.html" target="_blank">python基礎二</a></p>
<p><a style="text-decoration: underline;" href="https://www.cnblogs.com/jin-xin/articles/9439483.html" target="_blank">Python最詳細,最深入的程式碼塊小資料池剖析</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7738630.html" target="_blank">python集合,深淺copy</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8183203.html" target="_blank">python檔案操作</a></p>
<h4 style="background-color: #f08080;">python函式部分</h4>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8241942.html" target="_blank">python函式初識</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8259929.html" target="_blank">python函式進階</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8305011.html" target="_blank">python裝飾器</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8423526.html" target="_blank">python迭代器,生成器</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8423937.html" target="_blank">python內建函式,匿名函式</a></p>
<p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8743408.html" target="_blank">python遞迴函式</a></p>
<p><a style="text-decoration: underline;" href="https://www.cnblogs.com/jin-xin/articles/8743595.html" target="_blank">python二分查詢演算法</a></p>

'''
6.1、找到所有的p標籤
ret = re.findall('<p>.*?</p>', s1)
print(ret)

6.2、找到所有a標籤對應的url
print(re.findall('<a.*?href="(.*?)".*?</a>',s1))
print(re.findall('href="(.*?)"', s1))
View Code