一、正则表达式
(1)介绍
- 理解正则表达式:正则表达式是从左往右一个一个字符匹配的
- 作用
- 从大段字符串中找到符合规则的内容
- 判断字符串是否完全符合规则
- 定义
- 在编写处理字符串的程序或网页时,经常有查找符合某些复杂规则的字符串的需要。
- 正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
- 作用
(2)规则
-
匹配的规则:从左往右匹配,一个一个字符匹配
-
正则表达式中的一些概念
-
字符组:
[]
,可以匹配出一个字符,该字符是写在中括号内的(通过ASCCI码),例子如下[12345_()] --> 可以匹配出:1, 2, 3, 4, 5, _, (, ) [1-3] --> 可以匹配出:1, 2, 3 [a-c] --> 可以匹配出:a, b, c [a-cA-D1-3] --> 可以匹配出:a, b, c, A, B, C, D, 1, 2, 3
-
非字符组:
[^]
,和字符组相反-
例子如下
[^0-9] --> 可以匹配出所有非数字字符
-
-
注意:有些特殊字符进入字符组和非字符组时,会恢复原意,如
[|]
可以匹配|
-
元字符
- 转义字符:
\
会转义字符,想要取消某个转义,就用\\
.
匹配除换行符以外的任意字符\w
匹配字母或数字或下划线(w–>word)\s
匹配任意的空白符如\t,\n
(s–>space)\d
匹配数字(d–>digit)\W \S \D
这3个分别和\w \s \d
相反\b
匹配单词的开始或结束ed\b
匹配以ed
结尾的单词\bhe
匹配以he
开头的单词
^
匹配字符串的开始$
匹配字符串的结束a|b
匹配符合a规则或b规则的字符,注意先匹配a,如果符合a规则,则不检查b规则
- 转义字符:
-
量词:用来限定字符出现的次数,所有量词只约束前面的紧贴着该量词的元字符
{n
前面的元字符出现n次{n,}
前面的元字符至少出现n次{n,m}
前面的元字符出现n-m次?
前面的元字符出现0或1次+
前面的元字符1次或多次*
前面的元字符出现0次或多次
-
分组
- 给几个字符加上量词约束需求时,用分组可以同时约束这几个量词,即把这几个量词分到一个组里
-
(3)例子
- 匹配小数
\d+[.]\d+
或\d+\.\d+
- 匹配小数或整数
\d+(\.\d+)?
- 这里
?
同时约束\.
和\d+
- 匹配邮箱
[0-9a-zA-Z][\w\-\.]+@[a-zA-Z0-9\-]+(\.[0-9a-zA-Z]+)*\.[0-9a-zA-Z]{2,6}
(4)正则表达式的特点
- 贪婪匹配:通过回溯算法
- 非贪婪匹配/惰性匹配:在量词后面加上
?
- 待匹配的字符串:
asd12383248124813sda
- 正则表达式1(贪婪匹配):
\d+8
- 匹配结果是:
123832481248
(先把所有数字匹配,再回溯直到找到8)
- 匹配结果是:
- 正则表达式2(非贪婪匹配):
\d+?8
- 匹配结果是:
1238
3248
1248
(找到所有以8结尾的一串数字)
- 匹配结果是:
- 正则表达式1(贪婪匹配):
二、re模块
(1)模块简介
- 什么是模块
- 模块就是一组功能的集合
- 当我们想要和某样东西打交道,而这样东西和python没有关系时,python提供了一个功能的集合,负责与这样东西打交道
- 如os模块是与操作系统交互,可以让我们通过python操纵操作系统
- 模块的类型:
- 内置模块:解释器自带的
- 第三方模块:需要我们自己安装的
- 自定义模块:自己写的模块
- 模块就是一组功能的集合
(2)re模块的基本使用
-
re模块专门用来处理正则表达式
-
查找
-
re.findall([正则表达式], [待匹配的字符串], [])
- 匹配所有符合规则的字符串,返回的是一个列表
- 注意:正则表达式是一个字符串
-
re.search([正则表达式], [待匹配的字符串])
-
只匹配从左到右的第一个符合规则的字符串
-
返回的不是真正的结果,需要通过返回的变量的
group()
方法获取真正的结果 -
例子
In [1]: import re In [2]: ret = re.search("[a-z]{2}", "12abc43jk") In [3]: ret.group() Out[3]: 'ab'
-
-
-
字符串处理的扩展
-
切割:
re.split([正则表达式], [字符串])
-
根据正则表达式切割
-
注意:使用分组切割时,会保留被切割的字符串
-
例子
In [4]: s = "abc12de3fgh456ijk" In [5]: re.split("\d+", s) Out[5]: ['abc', 'de', 'fgh', 'ijk'] In [6]: re.split("(\d+)", s) # 被切割的数字也保留了 Out[6]: ['abc', '12', 'de', '3', 'fgh', '456', 'ijk']
-
-
替换:
re.sub([正则表达式], [准备替换的内容], [待替换字符串], count=0)
-
count为替换次数,返回替换的结果
-
例子
In [7]: old = "asd12da3AS4444qwe" In [8]: new = re.sub("\d+", " ", old, count=3) In [9]: new Out[9]: 'asd da AS qwe' In [10]: new = re.sub("\d+", " ", old, count=1) In [11]: new Out[11]: 'asd da3AS4444qwe'
-
-
re.subn()
- 和sub一样,只是返回的是结果及替换的次数(元组)
-
(3)re模块的进阶使用
-
re模块的进阶(节省时间/空间)
-
re.compile([正则表达式])
-
节省使用正则表达式解决问题的时间
-
编译:正则表达式编译成字节码
-
使用compile,则在多次使用正则表达式的过程中,不会多次编译
-
例子
In [12]: ret = re.compile("[a-z]{3,6}") In [13]: ret Out[13]: re.compile(r'[a-z]{3,6}', re.UNICODE) In [14]: ret.findall("12as323qwdsa23sdsa31") Out[14]: ['qwdsa', 'sdsa'] In [15]: ret.search("213add32djsaks2321") Out[15]: <_sre.SRE_Match object; span=(3, 6), match='add'> In [16]: ret.search("213add32djsaks2321").group() Out[16]: 'add'
-
-
re.finditer([正则表达式], [字符串])
-
返回的是一个可迭代对象(为了节省内存)
-
例子
In [22]: it = re.finditer("\d{1,3}", "adda12asdas23ada23223z") In [23]: it Out[23]: <callable_iterator at 0x10c366860> In [24]: it.__next__().group() Out[24]: '12' In [25]: it.__next__().group() Out[25]: '23' In [26]: it.__next__().group() Out[26]: '232' In [27]: it.__next__().group() Out[27]: '23' In [28]: it.__next__().group() --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-28-e688ae58b280> in <module>() ----> 1 it.__next__().group() StopIteration: # 或者 In [29]: it = re.finditer("\d{1,3}", "adda12asdas23ada23223z") In [30]: for ele in it: ...: print(ele.group()) ...: 12 23 232 23
-
-
(4)分组在re模块中的使用
-
分组在re模块中的使用
- 分组可以把得到的结果分别存储,我们根据需要取出结果
In [31]: s = "<a>who</a>" In [32]: ret = re.search("<(\w+)>(\w+)<(/\w+)>", s) In [33]: ret.group() Out[33]: '<a>who</a>' In [34]: ret.group(1) # 第一个分组是(\w+) Out[34]: 'a' In [35]: ret.group(2) # 第二个分组是(\w+) Out[35]: 'who' In [36]: ret.group(3) # 第三个分组是(/\w+) Out[36]: '/a'
-
findall的分组优先匹配原则
In [37]: s = "<a>who</a>" In [38]: ret = re.findall(">(\w+)<", s) In [39]: ret # 匹配到的是'>who<',但是findall优先显示分组(即(\w+))里的内容 Out[39]: ['who'] # 取消分组优先的方法: In [40]: s = "<a>who</a>" In [41]: re.findall(">(\w+)<", s) Out[41]: ['who'] In [42]: re.findall(">(?:\w+)<", s) # 加上?:即可 Out[42]: ['>who<']
-
分组命名的语法:
?P<name>
In [43]: s = "<a>who</a>" In [44]: ret = re.search("<(?P<str1>\w+)>(?P<str2>\w+)<(?P<str3>/\w+)>", s) In [45]: ret.group() Out[45]: '<a>who</a>' In [46]: ret.group("str1") Out[46]: 'a' In [47]: ret.group("str2") Out[47]: 'who' In [48]: ret.group("str3") Out[48]: '/a'
-
分组命名的一个应用
-
分组命名可以用于匹配html的标签,例子如下
In [49]: s1 = "<a>who</b>" In [50]: s2 = "<a>who</a>" In [51]: ret1 = re.search("<(?P<tab>\w+)>\w+</(?P=tab)>", s1) In [52]: ret2 = re.search("<(?P<tab>\w+)>\w+</(?P=tab)>", s2) In [53]: ret1 # 该字符串不符合标签配对的规则,所以没有被匹配出来 In [54]: ret2 Out[54]: <_sre.SRE_Match object; span=(0, 10), match='<a>who</a>'>
- 在这个例子中,使用分组命名帮我们检查了标签的格式是否符合规则
-