11. 正则表达式

基于网络课程《Python全栈开发专题》 记录笔记,请支持正版课程

  1. match:检测字符串是否匹配正则表达式
  2. search:在一个长的字符串中搜索匹配正则表达式的子字符串
  3. findall:查找字符串
  4. sub和subn:搜索和替换
  5. split: 通过正则表达式指定分隔符,通过这个分隔符将字符串拆分

文档

re模块官方文档: https://docs.python.org/3.6/library/re.html

match()

import re
m = re.match('hello', 'hello')
print(m)           # <_sre.SRE_Match object; span=(0, 5), match='hello'>
print(m.group())   # hello
# print(m.__class__)
print(type(m))     # <class '_sre.SRE_Match'>
print(m.__class__.__name__)  # SRE_Match

'''
如果匹配不到,m是 None
'''
m1 = re.match('abc', '1111')
if m1 is None:
    print('是的,没有匹配到')

search()

search()只返回第一个匹配成功的结果。

from re import search, match

m1 = match('Python', 'Leif is short, you need Python')
m2 = search('Python', 'Leif is short, you need Python')
m3 = search('Python', 'Leif is short, you need Python. Python again.')

if m1 is not None: print('m1: ', m1.group())
if m2 is not None: print('m2: ', m2.group())
if m2 is not None: print('m2: ', m2)
if m3 is not None: print('m2: ', m3)  # 只返回第一个匹配成功的结果

匹配多个字符"|"(或)

from re import match, search
s = 'Python|Ruby|Java|C\+\+'
 
m = match(s, 'hahaha ')
print(m)
m = search(s, 'I love Python.')
print(m)
m = search(s, 'I love Java.')
print(m)

匹配单个字符"."(一个字符,转义)

from re import match, search

s = 'bin.'
m = match(s, 'binxa')
if m is not None:
    print(m.group())  # binx
    
m = search(s, '<bind>')
print(m)

s1 = '3.14'
s2 = '3\.14'  # 3.14

m = match(s1, '3.14')
print(m)
m = match(s1, '3x14')
print(m)
m = match(s2, '3.14')
print(m)
m = match(s2, '3x14')
print(m)  # None

使用字符集"[]"(集合)

import re
m = re.match('[aAbB]', 'a')
print(m)

print(1, re.match('a|b|cC|d', 'a'))
print(2, re.match('a|b|cC|d', 'cC'))

print(3, re.match('[abcd]', 'ab'))
print(4, re.match('ab|cd', 'ab'))

print(5, re.match('[ab][cd][ef][gh]', 'aceh'))
print(6, re.match('[ab][cd][ef][gh]', 'aceX')) # None

重复、可选和特殊字符

  • *:0到n
  • +:1到n
  • ?:可选
'''
匹配'a' 'b' 'c'三个字母按顺序从左到右排列,而且这3个字母都必须至少有1个
abc  aabc   abbbccc   bca(不匹配)
'''
import re
s = 'a+b+c'
strList = ['abc','aabc','bbabc','aabbbcccxyz']
for value in strList:
    m = re.match(s, value)
    if m is not None: print(m.group())
    else: print('{}不匹配'.format(value))
'''
匹配任意3个数字-任意3个小写字母
123-abc   543-xyz   1-xyz(不匹配)  xyz-123(不匹配)

\d: 代表数字
\w: 代表字符(中文也行)
[a-z]{3}: 三个小写字母
'''
import re

s = r'\d{3}-[a-z]{3}'
strList = ['123-abc','456-xyz','1234-xyz','1-xyzabc','657-xyz^%abc']

for value in strList:
    m = re.match(s, value)
    if m is not None: print(m.group())
    else: print('{}不匹配'.format(value))
'''
匹配以a到z的26个字母中的任意一个作为前缀(也可以没有这个前缀),后面至少有1个数字
'''
import re

s = r'[a-z]?[0-9]+'
strList = ['1234','a123','ab456','b234abc']

for value in strList:
    m = re.match(s, value)
    if m is not None: print(m.group())
    else: print('{}不匹配'.format(value))
'''
匹配email
'''
import re

s = r'[\w-]+@[\w\.]+\.com'
# \w 中,不包含自fun
strList = ['abc@126.com','test@mail.geeori.com','test-abc@geekori1.com','abc@geekori.com.cn']

for value in strList:
    m = re.match(s, value)
    if m is not None: print(m.group())
    else: print('{}不匹配'.format(value))

分组

  1. 只有圆括号括起来的部分才算一组,如果正则表达式中既有被圆括号括起来的部分,
    也有未被圆括号括起来的部分,那么只将圆括号括起来的部分算一组
  2. group方法,如果不指定参数,会返回匹配的整个字符串,如果加参数,会返回指定
    分组的字符串,组索引从1开始
  3. groups方法,以元组形式返回匹配的所有分组
import re
m = re.match(r'(\d{3})-\d{4}-[a-z]{2}', '123-4567-xy')
print(m.groups())  # ('123',)
print(m.group(1))  # 123
print(m.group())   # 123-4567-xy

字符串的首尾和单词边界

  • "^":匹配字符串的开始
  • "$":匹配字符串的结束
  • "\b":匹配单词边界
import re
s = r'^\w+$'
strList = ['aklskjdfklajsdklfj=', '  a;lskdjf', 'alsdkjflkasdjf']
for value in strList:
    m = re.match(s, value)
    if m is not None: print(m.group())
    else: print('{}没有匹配到'.format(value))
    
print('=========================================')
    
s = r'\bthe\b'
strList = ['you are the man', 'The USA', 'theme']
for value in strList:
    m = re.search(s, value)   # 注意此处是search
    if m is not None: print(m.group())
    else: print('{}没有匹配到'.format(value))

findall 和 finditer

  • findall:通过列表返回所有满足条件的字符串,DOM
  • finditer:将搜索结果通过一个迭代器返回,SAX
import re

s = '12-a-abc54-A-xyz---78-A-ytr'
result = re.findall(r'\d\d-[aA]-[a-z]{3}', s)
print(result)  # ['12-a-abc', '54-a-xyz', '78-A-ytr']

# 如果正则表达式里面有分组,则返回的是个二维列表
result = re.findall(r'(\d\d-[aA])-([a-z]{3})', s)
print(result)  # [('12-a', 'abc'), ('54-a', 'xyz'), ('78-A', 'ytr')]

# re.I 忽略大小写: 这里的正则表达式没有A,但一样可以匹配到
result = re.findall(r'\d\d-a-[a-z]{3}', s, re.I)
print(result)  # ['12-a-abc', '54-A-xyz', '78-A-ytr']

it = re.finditer(r'(\d\d)-a-([a-z]{3})', s, re.I)
for result in it:
    print(result.group(), end = ' <')
    groups = result.groups()
    for i in groups:
        print(i, end = ' ')
    print('>')

使用sub和subn,搜索和替换

'''
sub(正则表达式,要替换的字符串,原字符串)
subn    多返回一个替换个数
'''
import re
result = re.sub('Bill', 'Mike', 'Bill is my son.')
print(result)  # Mike is my son.

# subn: 除了替换结果,也会返回替换个数
result, counter = re.subn('Bill', 'Mike', 'Bill is my son, I like Bill')
# print(result)
print(result)   # Mike is my son, I like Mike
print('替换了{}个字符'.format(counter))  # 替换了2个字符

'''
中间的参数也可以是正则表达式
'''
s = '([0-9])([a-z]+)'
result = re.sub(s, r'\1:\2', '01-1abc,02-2xyz,03-9hfg')
print(result)  # 01-1:abc,02-2:xyz,03-9:hfg

# 也可以放在函数里面
def fun():
    return r'\1:\2'
result = re.sub(s, fun(), '01-1abc,02-2xyz,03-9hfg')
print(result)   # 01-1:abc,02-2:xyz,03-9:hfg

使用split()函数分割字符串

from re import split

result = split(';', 'Bill; Mike; John')
print(list(result))   # ['Bill', ' Mike', ' John']

# 至少有1个逗号(,)或分号(;)或点(\.)或空白符(\s)
result = split(r'[,;\.\s]+','a,b,,,d.x,;ok')
print(result)         # ['a', 'b', 'd', 'x', 'ok']

# 第三个参数,只要前多少个值
# 返回的是列表,列表中最后一个元素是剩下的字符串
result = split(r'[,;\.\s]+', 'a,b,,,d.x,;ok', maxsplit=2)
print(result)         # ['a', 'b', 'd.x,;ok']

练习1

编写一个正则表达式,匹配这几个单词:bat、Bit、But、hAt、hit、hut。

'''
1.  编写一个正则表达式,匹配这几个单词:、、、、、。
'''
import re

strList = ['bat', 'Bit', 'But', 'hAt', 'hit', 'hut']
# s = '^[bh][aiu]t$'
s = r'[a-z]{3}'
for value in strList:
    # 加上 re.I 参数,忽略大小写
    m = re.match(s, value, re.I)
    if m is not None: print(m.group())
    else: print('{}无法匹配'.format(value))

练习2

'''
2.  编写一个正则表达式,匹配信用卡号。格式如下:
xxxx xxxx xxxx xxxx,其中x表示0到9的数字。每一组是4个数字,组与组之间需要有至少一个空格。
1234 4321 5432 1234
'''
import re

s = r'^(\d{4}\s){3}\d{4}$'
#s = '^\d{4}\s+\d{4}\s+\d{4}\s+\d{4}$'

def checkRegex(value):
    m = re.match(s, value)
    if m is not None: print('正确的信用卡号:{}'.format(m.group()))
    else: print('{} 格式错误!'.format(value))

# num = '1111 2222 3333 4444'
# m = re.match(s, num)
# print(m.groups())   # ('3333 ',)  !!!!!!!!!!!!!!

while True:
    num = input("输入一个信用卡号:")
    if num == 'exit()': break
    checkRegex(num)

练习3

'''
3. 编写一个匹配日期的正则表达式,日期格式:YYYY-MM?-DD?。
其中YYYY表示4位的年,MM?表示1位或2位的月,DD?表示1位或2位的日。
而且4位的年必须在2000年以后,包括2000年。
例如,2001-4-5、2004-05-1都符合要求。
'''

# 这个正则表达式不能排除14月,38号之类的错误。简单意思一下
s = r'^2\d{3}-[01]?\d-[1-3]?\d$'
# s = '2\d{3}-\d{1,2}-\d{1,2}'

from re import match

def isMatch(date):
    m = match(s, date)
    if m is not None: print('{} is a date.'.format(m.group()))
    else: print('Not matched: {}'.format(date))
    
isMatch('1999-12-31')
isMatch('2011-1-41')
isMatch('2050-14-5')
isMatch('2050-12-31')
isMatch('2050-12-39')
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • Python中的正则表达式(re) import rere.match #从开始位置开始匹配,如果开头没有则无re...
    BigJeffWang阅读 7,071评论 0 99
  • python的re模块--细说正则表达式 可能是东半球最详细最全面的re教程,翻译自官方文档,因为官方文档写的是真...
    立而人阅读 22,868评论 4 46
  • JS正则表达式一条龙讲解,从原理和语法到JS正则、ES6正则扩展,最后再到正则实践思路 Stinson 关注 20...
    小杰的简书阅读 658评论 0 2
  • 我今天做了一直不敢的决定,什么都没有还害怕输不起。笑话…
    海里会飞的鱼儿阅读 237评论 2 0
  • 1.类与实例 对象是一个自包含的实体,用一组可识别的行为和特性来标识。 类就是具有相同的属性和功能的对象的抽象的集...
    银河的精神家园阅读 241评论 0 0