第一课、初始正则表达式
一、课程介绍
1.1 课程概要
步骤介绍
- 正则表达式入门及应用
- 正则的进阶
- 案例
- 综合项目实战
二、正则表达式的基本操作(多敲代码多做练习)
2.1 什么是正则表达式
什么是正则表达式
- 正则表达式(简称为regex)是一些有字符和特殊符号组成的字符串
- 能按照某种模式匹配一系列有相似特征的字符串 例如:[a-z]表示26个小写英文字母
正则表达式中的符号
| 符号 | 描述 | 示例 |
| literal | 匹配文本字符串的字面值literal | foo |
| re1|re2 | 匹配正则表达式re1或者re2 | foo|bar |
| . | 匹配任何字符(除了\n之外) | b.b |
| ^ | 匹配字符串起始部分 | ^Dear |
| $ | 匹配字符串终止部分 | /bin/*sh$ |
| * | 匹配0次或者多次前面出现的正则表达式 | [A-Za-z0-9]* |
| + | 匹配1次或者多次前面出现的正则表达式 | [a-z]+\.com |
| ? | 匹配0次或者1次前面出现的正则表达式 | goo? |
| {N} | 匹配N次前面出现的正则表达式 | [0-9]{3} |
| {M,N} | 匹配M~N次前面出现的正则表达式 | [0-9]{5,9} |
| [...] | 匹配来自字符集的任意单一字符 | [aeiou] |
| [..x-y..] | 匹配x~y范围中的任意单一字符 | [0-9][A-Za-z] |
| [^...] | 不匹配此字符集中出现的任何一个字符,包括某一范围的字符(如果在此字符集中出现) | [^aeiou],[^A-Za-z0-9] |
| (*|+|?|{})? | 用于匹配上面频繁出现/重复出现符号的非贪婪版本(*、+、?、{}) | *?[a-z] |
| (...) | 匹配封闭的正则表达式,然后另存为子组 | ([0-9]{3})?,f(oo|u)bar |
正则表达式中的特殊字符
| 特殊字符 | 描述 | 示例 |
| \d | 匹配任何十进制数字,与[0-9]一致(\D与\d相反,不匹配任何非数值型的数字) | data\d+.txt |
| \w | 匹配任何字母数字字符,与[A-Za-z0-9]相同(\W与之相反) | [A-Za-z_]\w+ |
| \s | 匹配任何空格字符,与[\n\t\r\v\f]相同(\S与之相反) | of\sthe |
| \b | 匹配任何单词边界(\B与之相反) | \bThe\b |
| \N | 匹配已保存的子组N(参见上面的(...)) | price:\16 |
| \c | 逐字匹配任何特殊字符c(即,仅按照字面意义匹配,不匹配特殊含义) | \.,\\,\* |
| \A(\Z) | 匹配字符串的起始(结束)(另见上面介绍的^和$) | \ADear |
正则表达式中的扩展表示法
| 扩展表示法 | 描述 | 示例 |
| (?iLmsux) | 在正则表达式中嵌入一个或者多个特殊"标记"参数(或者通过函数/方法) | (?x),(?im) |
| (?:…) | 表示一个匹配不用保存的分组 | (?:\w+\.)* |
| (?P<name>…) | 像一个仅由name标识而不是数字ID标识的正则分组匹配 | (?P<data>) |
| (?P=name) | 在同一字符串中匹配由(?P<name)分组的之前文本 | (?P=data) |
| (?#…) | 表示注释,所有内容都被忽略 | (?#comment) |
| (?=…) | 匹配条件是如果…出现在之后的位置,而不使用输入字符串;称作正向前视断言 | (?=.com) |
| (?!…) | 匹配条件是如果…不出现在之后的位置,而不使用输入字符串;称作负向前视断言 | (?!.net) |
| (?<=…) | 匹配条件是如果…出现在之前的位置,而不使用输入字符串;称作正向后视断言 | (?<=800-) |
| (?<!…) | 匹配条件是如果…不出现在之前的位置,而不使用输入字符串;称作负向后视断言 | (?<!192\.168\.) |
| (?(id/name)Y|N) | 如果分组所提供的id或name(名称)存在,就返回正则表达式的条件匹配Y,如果不存在,就返回N;|N是可选项 | (?(1)y|x) |
使用正则表达式
- 简单匹配:例如:abc=>abc
- 多个匹配模式:例如:abc|12c=>abc,12c
- 匹配任意字符(.),除\n之外:例如:.=>abc.12c
2.2 正则表达式的使用
使用正则表达式
- *匹配0次或者多次
- +匹配一次或者多次
- ?匹配0次或者一次
- {N}匹配指定的N次
- {M,N}匹配M-N次,最大化优先
2.3 正则表达式匹配同类型及边界匹配
匹配同类型
- \d匹配数字
- \w匹配数字和字母
- \s匹配任何空格字符
边界匹配
- 用^匹配以**开头
- 用$匹配以**结尾
2.4 正则表达式匹配选项
匹配特殊字符
- 需要用“\”进行转义
- 使用[]指定要匹配的集合
- 使用[^]指定不要匹配的内容
2.5 正则表达式分组
正则表达式分组
- 重复一个字符串时
- 使用()进行分组,使用(?<word>\w+)指定组名
- 从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推
- 使用()对匹配的内容分组
- 使用\1、\2反向引用
2.6 贪婪模式vs非贪婪模式
贪婪模式vs非贪婪模式
- 贪婪匹配
- 在整个表达式匹配成功的前提下,尽可能多的匹配
- 非贪婪匹配
- 在整个表达式匹配成功的前提下,以最少的匹配字符
- 默认是贪婪模式
- 贪婪模式举例
- 表达式ab.+c
测试数据:abacaxcd
匹配结果:abacaxc
- 非贪婪模式
- 只需要在匹配pattern中加上“?”
- 表达式ab.*?c
测试数据:abacaxcd
匹配结果:abac
三、 实战:正则表达式的应用
实战:身份证号码的正则匹配
正则表达式联系
- 身份证号码匹配正则表达式编写
- 电子邮箱正则表达式
四、 正则表达式进阶
4.1 正则的进阶介绍
课程概要
- re模块
- findall()的使用
- search()的使用
- group()与groups()的使用
- split()正则分割
- Sub()正则替换
课程目标
- 掌握compile()和match()函数的使用
- 掌握findall()和search()函数的使用
- math()和search()的区别
- 掌握group()、groups()、groupdict()的基本使用
- 掌握split()、sub()的基本使用
教学思路
- 先理论
- 后代码实战
- 跟着来
4.2 re模块介绍
re模块
| 属性 | 描述 |
| re.I、re.IGNORECASE | 不区分大小写的匹配 |
| re.L、re.LOCALE | 根据所使用的本地语言环境通过\w、\W、\b、\B、\s、\S实现匹配 |
| re.M、re.MULTILINE | ^和$分别匹配目标字符串中行的起始和结尾,而不是严格匹配整个字符串本身的起始和结尾 |
| re.S、re.DOTALL | “.”(点号)通常匹配除了\n(换行符)之外的所有单个字符;该标记表示“.”(点号)能够匹配全部字符 |
| re.X、re.VERBOSE | 通过反斜杠转义,否则所有空格加上#(以及在该行中所有后续文字)都被忽略,除非在一个字符类中或者允许注释并且提高可读性 |
re模块-compile
- compile(pattern,flag = 0)
- 使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象
- 推荐编译,但它不是必须的
re模块-match
- match(pattern,string,flag = 0)
- 尝试使用带有可选的标记的正则表达式的模式来匹配字符串。如果匹配成功,就返回匹配对象;如果失败,就返回None
1 import re 2 3 # 将正则表达式编译 4 pattern = re.compile(r'hello', re.I) 5 print(dir(pattern)) 6 7 # 通过match进行匹配 8 rest = pattern.match('Hello,world') 9 print(rest) 10 print(dir(rest)) 11 print('string', rest.string) 12 print('re:', rest.re) 13 print(rest.groups())
4.3 findall和search
findall()的使用
- findall(pattern,string[,flags])
- 查找字符串中所有(非重复)出现的正则表达式模式,并返回一个匹配列表
1 import re 2 3 # 找出一下字符串中的数字 4 5 content = 'One1Two22three333four4444five55555six666666' 6 7 # 使用编译的对象 8 p = re.compile(r'[a-z]+', re.I) 9 rest = p.findall(content) 10 print(rest) 11 12 # 不编译 13 all_rest = re.findall(r'[a-z]+', content, re.I) 14 print(all_rest)
search()的使用
- search(pattern,string, flags = 0)
- 使用可选标记搜索字符串中第一次出现的正则表达式模式。如果匹配成功,则返回匹配对象;如果失败,则返回None
1 import re 2 3 # 找出一下字符串中的数字 4 5 content = 'hello, world' 6 7 # 使用编译的对象 8 p = re.compile(r'world') 9 # 使用search 10 rest = p.search(content) 11 print(rest) 12 13 # 使用match 14 rest_match = p.match(content) 15 print(rest_match) 16 17 # 使用函数进行调研 18 rest_func = re.search(r'world', content) 19 print(rest_func)
4.4 正则表达式的分组(group()与groups())
group()与groups()的使用
- group(num)返回整个匹配对象或编号为num的特定子组
- groups():返回一个包含所有匹配子组的元组(如果没有成功匹配,则返回一个空组)
1 import re 2 3 def test_group(): 4 content = 'hello, world' 5 p=re.compile(r'world') 6 rest = p.search(content) 7 print(rest) 8 if rest: 9 # group的使用 10 print(rest.group()) 11 # groups的使用 12 print(rest.groups()) 13 14 15 16 def test_id_card(): 17 """身份证号码正则匹配""" 18 # p = re.compile(r'(\d{6})(\d{4})((\d{2})(\d{2}))(\d{2}\d{1})([0-9]|X)') 19 p = re.compile(r'(\d{6})(?P<year>\d{4})((?P<mouth>\d{2})(?P<day>\d{2}))(\d{2}\d{1})([0-9]|X)') 20 # 准备两个身份证号 21 id ='230381199310070329' 22 rest1 = p.search(id) 23 print(rest1.group(2)) 24 print(rest1.groups()) 25 print(rest1.groupdict()) 26 27 if __name__ == '__main__': 28 #test_group() 29 test_id_card()
4.5 正则表达式的分割和替换
split()正则分割
- split(pattern, string, max=0)
- 根据正则表达式的模式分隔符,split函数将字符串分割为列表,然后返回成功匹配的列表,分割最多操作max次(默认分割所有匹配成功的位置)
1 import re 2 3 """ 4 使用split正则分割字符串 5 """ 6 7 s = 'one1two2three3four4five5six6' 8 p = re.compile(r'\d+') 9 rest = p.split(s, 2) 10 print(rest)
sub()正则替换
- sub(pattern,repl,string,max=0)
- 使用repl替换string中每一个匹配的子串后返回替换后的字符串,最多操作max次(默认替换所有)
1 import re 2 3 """ 4 使用正则表达式进行替换 5 """ 6 s = 'one1two2three3four4five5six6' 7 # one@two@three@four@five@six@ 8 9 #使用正则替换 10 p = re.compile(r'\d+') 11 rest = p.sub('@', s) 12 print(rest) 13 14 # 使用字符串原始替换方式 15 rest_origin = s.replace('1', '@').replace('2', '@').replace('3', '@').replace('4', '@').replace('5', '@').replace('6', '@') 16 print(rest_origin) 17 18 s2 = 'hello world' 19 # 使用正则表达式 20 p2 = re.compile(r'(\w+) (\w+)') 21 rest_pos = p2.sub(r'\2 \1', s2) 22 print(rest_pos) 23 24 25 # 在原有的内容基础上,替换并改变内容 26 def f(m): 27 """使用函数进行替换规则改变""" 28 return m.group(2).upper() + ' ' + m.group(1) 29 30 31 rest_change = p2.sub(f, s2) 32 print(rest_change) 33 34 35 # 使用匿名函数进行替换 lambda 36 rest_lamb = p2.sub(lambda m: m.group(2).upper() + ' ' + m.group(1), s2) 37 print('--------') 38 print(rest_lamb)
五、 正则综合实战(多练多看)
5.1 实战:正则匹配图片地址的需求分析
# 1. 下载html
# 2. 写正则的规则
# 要找到img标签
# 找到src属性
# <img class="" style="" src="xxx" xx="">
# <img.+src=\".+\".+>
5.2 实战:代码实现正则匹配图片地址
1 import re 2 3 def test_re_img(): 4 """ 使用正则表达式找到图片的地址 """ 5 # 1.读取html 6 with open('img.html', encoding='utf-8') as f: 7 html = f.read() 8 # print(html) 9 # 2. 准备正则 10 p = re.compile(r'<img.+?src=\"(?P<src>.+?)\".+?>', re.I) 11 # 使用findall找到图片的列表 12 list_img = p.findall(html) 13 print(len(list_img)) 14 for ls in list_img: 15 print(ls.replace('&', '&')) 16 #TODO 使用urllib,requests将图片保存 17 18 19 20 if __name__ == '__main__': 21 test_re_img()
六、 课程总结
课程总结
- 什么是正则表达式
- 正则表达式匹配N次
- 匹配同类型及边界匹配
- 正则表达式匹配选项
- 正则表达式分组
- 贪婪模式VS非贪婪模式
- 正则表达式分析练习
知识点回顾
- 什么是正则表达式
- 正则表达式(简称为regex)是一些由字符和特殊符号组成的字符串
- 能按照某种模式匹配一系列有相似特征的字符串
- 例如:[a-z]表示26个小写字母
- 使用正则表达式
- 简单匹配:例如:abc => abc
- 多个匹配模式:例如:abc|12c =>abc,12c
- 匹配任意字符(.),除\n之外:例如:. => abc, 12c
- *匹配0次或者多次
- +匹配一次或者多次
- ?匹配0次或者1次
- {N}匹配指定的N次
- {M-N}匹配M-N次,最大化优先
- 匹配同类型
- \d匹配数字
- \w匹配数字和字母
- \s匹配任何空格字符
- 边界匹配
- 用^匹配以**开头
- 用$匹配以**结尾
- 匹配特殊字符
- 需要用“\”进行转义
- 指定匹配选项
- 使用[]指定要匹配的集合
- 使用[^]指定不要匹配的内容
- 正则表达式分组
- 重复一个字符串时
- 使用()进行分组,使用(?<word>\w+)指定组名
- 从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推
- 使用()对匹配的内容分组
- 使用\1、\2反向引用
- 贪婪模式VS非贪婪模式
- 贪婪模式
- 在整个表达式匹配成功的前提下,尽可能多的匹配
- 非贪婪模式
- 在整个表达式匹配成功的前提下,以最少的匹配字符
- 只需在匹配pattern中加上“?”
- 表达式ab.*?c
- 测试数据:abacaxcd
- 匹配数据:abac
- 默认是贪婪模式
- 贪婪模式
- 正则表达式练习
- 身份证号码匹配正则表达式编写
- 电子邮箱正则表达式编写
- 手机号码正则表达式编写
重点知识
- 理解什么是正则表达式以及它的用途
- 正则表达式的基本语法
- 正则表达式的编写和验证
- 正则表达式的分组及反向引用
难点知识
- 正则表达式的贪婪与非贪婪模式
- 正则表达式的分析方法
浙公网安备 33010602011771号