Python學習(十三)--正則表達式

1.概述

正則表達式是一個特殊的字符序列,一個字符串是否與我們所設定的字符序列相匹配

主要用于快速檢索文本、實現一些替換文本的操作
例如:
(1)檢查一串數字是否是電話號碼
(2)檢查一個字符串是否符合email
(3)把一個文本里指定的單詞替換為另一個單詞

2.初識

不使用正則表達式查找字符串中某個單詞

    a = 'C|C++|Java|Python|C#|Javascript'
    print(a.index('Python') > -1)
    print('Python' in a)
==>
    True
    True

使用正則表達式:要用到Python的模塊re

import re

a = 'C|C++|Java|Python|C#|Javascript'
r = re.findall('Python',a)
print(r)
if len(r) > 0:
    print('字符串中包含Python')
else:
    print('No')
==>
['Python']
字符串中包含Python

3.數字正則表達式

b = 'C0C++7Java8Python9C#6Javascript'
需求:找出字符串中所有的數字
(1)方法1:for in 循環找出所有的數字
(2)方法2:

import re

b = 'C0C++7Java8Python9C#6Javascript'
re.findall('0' , b)
re.findall('1' , b)
re.findall('2' , b)
re.findall('3' , b)
.....
re.findall('9' , b)

(3)方法3:

import re

b = 'C0C++7Java8Python9C#6Javascript'
r = re.findall('\d' , b)    #\d表示所有的數字
print(r)
==>
['0', '7', '8', '9', '6']

4.正則表達式類型

(1)普通字符 'Python'
(2)元字符
例如:'\d' 匹配一個數字字符。等價于[0-9]
'\D' 匹配一個非數字字符。等價于[^0-9]
(3)普通字符和元字符混合
參考鏈接:https://baike.baidu.com/item/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F
https://www.runoob.com/regexp/regexp-metachar.html

5.字符集

import re

s = 'abc, acc, adc, aec, afc, ahc'
#找出所有中間字母為c或者f的單詞
r = re.findall('a[cf]c', s)
print(r)
#找出所有中間字符非c或f或d的單詞
r1 = re.findall('a[^cdf]c',s)
print(r1)
#找出所有中間字符為c或d或e或f的單詞
r2 = re.findall('a[c-f]c',s)
print(r2)
==>
['acc', 'afc']
['abc', 'aec', 'ahc']
['acc', 'adc', 'aec', 'afc']

6.概括字符集

'\d' 匹配一個數字字符。等價于[0-9]
'\D' 匹配一個非數字字符。等價于[^0-9]
[\w] 匹配包括下劃線的任何單詞字符。類似但不等價于“[A-Za-z0-9_]”,這里的"單詞"字符使用Unicode字符集,例如 & 是匹配不到的
[\W] 匹配任何非單詞字符。等價于[^A-Za-z0-9_] 包含空格、回車、制表符等
[\s] 匹配任何不可見字符,包括空格、制表符、換頁符等等。等價于[ \f\n\r\t\v]
[\S] 匹配任何可見字符。等價于[^ \f\n\r\t\v]
.點 匹配除“\n”和"\r"之外的任何單個字符。要匹配包括“\n”和"\r"在內的任何字符,請使用像“[\s\S]”的模式

a = 'C#\rpython_11&11 java\n678\tphp'
r1 = re.findall('[0-9]',a)
print(r1)
r2 = re.findall('[^0-9]',a)
print(r2)
r3 = re.findall('\w',a)
print(r3)
r4 = re.findall('\W',a)
print(r4)
r5 = re.findall('\s',a)
print(r5)
r6 = re.findall('\S',a)
print(r6)
r7 = re.findall('.',a)
print(r7)
==>
['1', '1', '1', '1', '6', '7', '8']
['C', '#', '\r', 'p', 'y', 't', 'h', 'o', 'n', '_', '&', ' ', 'j', 'a', 'v', 'a', '\n', '\t', 'p', 'h', 'p']
['C', 'p', 'y', 't', 'h', 'o', 'n', '_', '1', '1', '1', '1', 'j', 'a', 'v', 'a', '6', '7', '8', 'p', 'h', 'p']
['#', '\r', '&', ' ', '\n', '\t']
['\r', ' ', '\n', '\t']
['C', '#', 'p', 'y', 't', 'h', 'o', 'n', '_', '1', '1', '&', '1', '1', 'j', 'a', 'v', 'a', '6', '7', '8', 'p', 'h', 'p']
['C', '#', '\r', 'p', 'y', 't', 'h', 'o', 'n', '_', '1', '1', '&', '1', '1', ' ', 'j', 'a', 'v', 'a', '6', '7', '8', '\t', 'p', 'h', 'p']

7.數量詞

(1)?
當該字符緊跟在任何一個其他限制符(*,+,?,{n},{n,},{n,m})后面時,匹配模式是非貪婪的。非貪婪模式盡可能少地匹配所搜索的字符串,而默認的貪婪模式則盡可能多地匹配所搜索的字符串。例如,對于字符串“oooo”,“o+”將盡可能多地匹配“o”,得到結果[“oooo”],而“o+?”將盡可能少地匹配“o”,得到結果 ['o', 'o', 'o', 'o']

貪婪:盡可能匹配最大設置數量來做匹配,盡可能匹配更多,滿足3時,不會立刻滿足并返回結果,還會繼續往下匹配,因此會匹配出python
例如:[a-z]{3,6} 會盡可能按照[a-z]{6}做匹配
非貪婪:[a-z]{3,6}? 一旦滿足3,就返回結果了

import re

a = 'ptho0python1pythonn2'

r = re.findall('[a-z]{3,6}',a)
print(r)

r1 = re.findall('[a-z]{3,6}?',a)
print(r1)
==>
['ptho', 'python', 'python']
['pth', 'pyt', 'hon', 'pyt', 'hon']

(2)*
匹配前面的子表達式任意次。例如,zo能匹配“z”,也能匹配“zo”以及“zoo”。等價于{0,}

import re

a = 'pytho0python1pythonn2'
r3 = re.findall('python*',a)
print(r3)
==>
['pytho', 'python', 'pythonn']

(3)+
匹配前面的子表達式一次或多次(大于等于1次)。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等價于{1,}

import re

a = 'pytho0python1pythonn2'
r3 = re.findall('python+',a)
print(r3)
==>
['python', 'pythonn']

(4)?
匹配前面的子表達式零次或一次。例如,“do(es)?”可以匹配“do”或“does”。?等價于{0,1}

import re

a = 'pytho0python1pythonn2'
r3 = re.findall('python?',a)
print(r3)
==>
['pytho', 'python', 'python']

8.邊界匹配

qq = '100001'
需求:校驗QQ號是否合規,要求QQ號必須在4-8位之間
錯誤方法:

r = re.findall('\d{4,8}',qq)
#qq號小于4位時,能篩選出錯誤的qq號
#當qq = '1000000001'  為9位時,上方語句也能匹配出來,這樣就錯了

正確方法:

#從行首開始匹配,滿足4-8字符,同時后邊緊跟著行尾
r = re.findall('^\d{4,8}$',qq)

^:匹配輸入字行首。如果設置了RegExp對象的Multiline屬性,^也匹配“\n”或“\r”之后的位置
$:匹配輸入行尾。如果設置了RegExp對象的Multiline屬性,$也匹配“\n”或“\r”之前的位置

qq = '100001'
r1 = re.findall('^000',qq)
print(r1)
r2 = re.findall('000$',qq)
print(r2)
==>
[]
[]

9.以單詞為最小單位匹配,用()括起來即可

a = 'PythonPythonPythoPythonPythonPython'
#匹配是否有連續3個Python
r = re.findall('(Python){3}',a)
print(r)
r1 = re.findall('(Python){4}',a)
print(r1)
==>
['Python']
[]

10.匹配模式

language = 'PythonC#\nJavaPHP'
r = re.findall('c#',language)
print(r)
==>
[]
#匹配不到
修飾符 描述
re.I 使匹配對大小寫不敏感
re.S 使 . 匹配包括換行在內的所有字符
re.L 做本地化識別(locale-aware)匹配
re.M 多行匹配,影響 ^ 和 $
re.U 根據Unicode字符集解析字符。這個標志影響 \w, \W, \b, \B.
re.X 該標志通過給予你更靈活的格式以便你將正則表達式寫得更易于理解。

使用re.I使匹配對大小寫不敏感

language = 'PythonC#\nJavaPHP'
r = re.findall('c#',language,re.I)
print(r)
==>
['C#']

需求:匹配到c#和其后邊的一個換行符

language = 'PythonC#\nJavaPHP'
r = re.findall('c#.{1}',language,re.I)
==>
[]
#匹配不到,因為.點無法匹配“\n”和“\r”這種字符

使用re.S使 . 匹配包括換行在內的所有字符

language = 'PythonC#\nJavaPHP'
r = re.findall('c#.{1}',language,re.I | re.S)
print(r)
==>
['C#\n']

11.re.sub 正則替換

import re

language = 'PythonC#JavaC#PHPC#'
r = re.sub('C#','GO',language)
#上述語句等同于r = re.sub('C#','GO',language,0),意思是字符串的C#全部替換
print(r)
==>
PythonGOJavaGOPHPGO

可以選擇不全部替換'C#'

language = 'PythonC#JavaC#PHPC#'
r = re.sub('C#','GO',language,1)
print(r)
==>
PythonGOJavaC#PHPC#
language = 'PythonC#JavaC#PHPC#'
r = re.sub('C#','GO',language,2)
print(r)
==>
PythonGOJavaGOPHPC#

全部替換也可以用另一個函數:replace

language = language.replace('C#','GO')
print(language)
==>
PythonGOJavaGOPHPGO

sub函數的第二個參數不僅可以是一個常量,也可以是一個函數
例如:

import re
    
def convert(value):
    pass
language = 'PythonC#JavaC#PHPC#'
r = re.sub('C#',convert,language)
print(r)
==>
PythonJavaPHP

源字符串如果有'C#',則'C#'會作為函數convert的入參,convert的輸出則將替換'C#',上方因為convert輸出為空,因此所有'C#'消失了
打印一下函數convert的入參value

def convert(value):
    print(value)
==>
<re.Match object; span=(6, 8), match='C#'>
<re.Match object; span=(12, 14), match='C#'>
<re.Match object; span=(17, 19), match='C#'>

原來value不單純只是一個字符串,而是一個對象,那么怎么直接拿到對象的內容?使用group()函數

def convert(value):
    matched = value.group()
    return '!!'+ matched + '!!'
language = 'PythonC#JavaC#PHPC#'
r = re.sub('C#',convert,language)
print(r)
==>
Python!!C#!!Java!!C#!!PHP!!C#!!

12.練習

字符串s = 'A83C27D1D8E67'
按如下規則進行字符串轉換:
(1)找到所有數字,數字為1位則刪掉
(2)數字為2位,則判斷數字是否大于等于50,若是,則替換為99;若不是,則替換為00
答案:

s = 'A83C27D1D8E67'
def convert(value):
    matched = value.group()
    if len(matched) < 2:
        return ''
    elif int(matched) >= 50:
        return '99'
    else:
        return '00'
r = re.sub('\d{1,2}',convert,s)
print(r)
==>
A99C00DDE99

13.match和search

s = 'A83CF8943GH44'
#match從字符串的第一個字符開始匹配,匹配不到\d就返回None
r1 = re.match('\d',s)
print(r1)
#search從整個字符串進行搜索,當搜索到第一個符合條件的字符時就返回該字符,并停止搜索
r2 = re.search('\d',s)
print(r2)
==>
None
<re.Match object; span=(1, 2), match='8'>

14.group()

import re

s = 'life is short,i use python'
s1 = 'life is short,i use python,i love python'

r = re.findall('life(.*)python',s)
print(r)

r = re.search('life(.*)python(.*)python',s1)
print(r.group(0))
print(r.group(1))
print(r.group(2))
print(r.groups())
==>
[' is short,i use ']      # 用圓括號括起來,可以不輸出邊界,只輸出想要的部分
life is short,i use python,i love python      #group(0)默認輸出全部
 is short,i use      #group(1)輸出第一個圓括號中的內容
,i love      #group(2)輸出第二個圓括號中的內容
(' is short,i use ', ',i love ')      #groups()把兩個括號中的內容合成一個元組輸出

15.JSON是一種輕量級的數據交換格式

(1)和XML相比,JSON很輕量
字符串是JSON的表現形式
符合JSON格式的字符串,就是JSON字符串
JSON是跨語言的,在每一種語言基本都能找到相對應的數據格式

import json

json_str = '{"name":"huluwa", "age":18}'
json_str1 = '[{"name":"huluwa", "age":18, "flag":false},{"name":"test", "age":28}]'
# 注意點:
# 1.key一定要用雙引號引起來,單引號不行(與語言無關)
# 2.字符串一定要用雙引號引起來,單引號不行(與語言無關),數字不需要引起來
# 3.python內部有雙引號,最外部一定要用單引號(與python有關)
student = json.loads(json_str)
print(type(student))
print(student)

student1 = json.loads(json_str1)
print(type(student1))
print(student1)

==>
<class 'dict'>
{'name': 'huluwa', 'age': 18}
<class 'list'>
[{'name': 'huluwa', 'age': 18, 'flag': False}, {'name': 'test', 'age': 28}]

(2)反序列化

# 從字符串轉化成語言下的某種數據類型的過程,叫做反序列化,反之則叫做序列化;
# json.loads()就是一個反序列化的過程,下面來看一下序列化:

student2 = [
    {'name':'huluwa','age':18,'flag':False},
    {'name':'test','age':28}
]

print(type(student2))
json_str2 = json.dumps(student2)
print(type(json_str2))
print(json_str2)
==>
<class 'list'>      #student2本來是list類型
<class 'str'>      #經過json.dumps()之后變成了字符串類型
[{"name": "huluwa", "age": 18, "flag": false}, {"name": "test", "age": 28}]

(3)JSON和python數據類型對應列表

JSON python
object dict
array list
string str
number int
number float
true True
false False
null None
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370