一、基本概念
1.1 什么是正则表达式
正则表达式(Regular Expression)也叫做匹配模式。它是由一组具有特定含义的字符串组成,通常用于匹配和替换文本。正则表达式是一门独立的技术,被很多编程语言实现了。
1.2 为什么使用正则表达式
在软件开发过程中,经常会涉及到大量的关键字等各种字符串的操作,使用正则表达式能很大程度的简化开发的复杂度和开发的效率,所以在 Python中正则表达式在字符串的查询匹配操作中占据很重要的地位。
1.3 Python中的re模块
>>> import re
>>> dir(re)
['A', 'ASCII', 'DEBUG', 'DOTALL', 'I', 'IGNORECASE', 'L', 'LOCALE', 'M', 'MULTILINE', 'Match', 'Pattern', 'RegexFlag', 'S', 'Scanner', 'T', 'TEMPLATE', 'U', 'UNICODE', 'VERBOSE', 'X', '_MAXCACHE', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', '_cache', '_compile', '_compile_repl', '_expand', '_locale', '_pickle', '_special_chars_map', '_subx', 'compile', 'copyreg', 'enum', 'error', 'escape', 'findall', 'finditer', 'fullmatch', 'functools', 'match', 'purge', 'search', 'split', 'sre_compile', 'sre_parse', 'sub', 'subn', 'template']
常用方法:
- findall() 查找所有,返回一个列表
- finditer() 查找所有,返回一个迭代器
- match() 从头匹配,匹配以xxx开头
- search() 只会查找一次
- split() 分割
- sub() 替换
举个例子:
>>> s = "今天天气真好"
>>> res = re.match("今天", s)
>>> res
<re.Match object; span=(0, 2), match='今天'>
>>> res.group()
'今天'
>>> res1 = re.match("天气", s) #以"天气"开头,匹配为none
>>> res1
>>>
>>> s
'啊10环,你比9环多1环'
>>> re.sub("10", "1", s)
'啊1环,你比9环多1环'
>>> re.split("\.", "www.baidu.com")
['www', 'baidu', 'com']
二、元字符
单个字符的匹配
代码 | 说明 |
---|---|
. | 任意符号(除换行符 \n) |
\d | 匹配数字 |
\w | 匹配有效符号(大小写字母、数字、下划线、各国语言符号) |
\s | 匹配空白位(空格、\t) |
^ | 以xx开头 |
$ | 以xx结尾,8$ 以8结尾 |
[] | 列举。其中 - 是在acsii表中的范围 |
# \d 匹配数字,其他均不匹配
>>> re.match("\d", "5")
<re.Match object; span=(0, 1), match='5'>
>>> re.match("\d", "a")
>>> re.match("\d", "A")
>>> re.match("\d", "+")
>>>
# . 匹配任意字符,除换行符
>>> re.match(".", "1")
<re.Match object; span=(0, 1), match='1'>
>>> re.match(".", "a")
<re.Match object; span=(0, 1), match='a'>
>>> re.match(".", "A")
<re.Match object; span=(0, 1), match='A'>
>>> re.match(".", "+")
<re.Match object; span=(0, 1), match='+'>
>>> re.match(".", "\n")
>>> #"\n"没有匹配
# \w 匹配有效字符
>>> re.match("\w", "a")
<re.Match object; span=(0, 1), match='a'>
>>> re.match("\w", "A")
<re.Match object; span=(0, 1), match='A'>
>>> re.match("\w", "_")
<re.Match object; span=(0, 1), match='_'>
>>> re.match("\w", "9")
<re.Match object; span=(0, 1), match='9'>
>>> re.match("\w", "+")
>>> #没有匹配,返回None
# \s 匹配空白符
>>> re.match("\s", " 123")
<re.Match object; span=(0, 1), match=' '>
>>> re.match("\s", "\t 123")
<re.Match object; span=(0, 1), match='\t'>
>>> re.match("\s", "abc\t 123")
>>>
# ^ 以xx开头 match本来就有从头匹配的功能,所以用findall配合使用
>>> re.findall("^1.*", "1abc") #以1开头,后面任意
['1abc']
>>> re.findall("^1.*", "abc")
[]
# $ 以xx结尾
>>> re.match("^1\d*8$", "18") #以1开头,以8结尾。中间全部是数字
<re.Match object; span=(0, 2), match='18'>
>>> re.match("^1\d*8$", "12348")
<re.Match object; span=(0, 5), match='12348'>
>>> re.match("^1\d*8$", "1abc8")
>>>#没有匹配,返回None
# [] 列举
>>> re.match("[0123456789]", "1") #[0123456789]等效于\d
<re.Match object; span=(0, 1), match='1'>
>>> re.match("[0123456789]", "a123")
>>>#没有匹配,返回None
>>> re.match("[0123abc]", "a123") #[0123abc]其中任意一个开头即可
<re.Match object; span=(0, 1), match='a'>
>>> re.match("[a-z]", "a123") #-符号是按照ascii表范围定义
<re.Match object; span=(0, 1), match='a'>
>>> re.match("[A-Z]", "ABCa123")
<re.Match object; span=(0, 1), match='A'>
>>> re.match("[0-9]", "123")
<re.Match object; span=(0, 1), match='1'>
>>> re.match("[A-Za-z]", "abc123") #大小写符号
<re.Match object; span=(0, 1), match='a'>
>>> re.match("[A-Za-z0-9_]", "ABCabc123") #有效符号
<re.Match object; span=(0, 1), match='A'>
- 匹配中文:
>>> re.match("[\u4e00-\u9fa5]", "中文")
<re.Match object; span=(0, 1), match='中'>
>>> re.match("[\u4e00-\u9fa5]", "abc中文")
>>>
三、反义符
也是元字符:单个字符的匹配
代码 | 说明 |
---|---|
\D | 非数字 |
\W | 特殊符号 |
\S | 非空白位 |
[^abc] | 不能是abc其中任意一个 |
#\D匹配非数字
>>> re.match("\D", "abc")
<re.Match object; span=(0, 1), match='a'>
>>> re.match("\D", "123")
>>>
#\W特殊符号
>>> re.match("\W", "???")
<re.Match object; span=(0, 1), match='?'>
>>> re.match("\W", "abc")
>>> re.match("\W", "123")
>>>
#\S非空白位
>>> re.match("\S", " ")
>>> re.match("\S", "\t")
>>> re.match("\S", "as")
<re.Match object; span=(0, 1), match='a'>
四、转译符
在Python中 \ 符号本身具有特殊意义。
>>> s = "hello \nworld"
>>> print(s)
hello
world
#如果字符串内容原本就包含 \nworld,添加\进行转义。
#第一个\是转译符,第二个\就是普通的\(不具有特殊意义)。
>>> ss = "hello \\nworld"
>>> print(ss)
hello \nworld
在python的字符串中\是具有特殊含义的,要正常表示一个\,需要两个\来表示。在正则中,\是具有特殊含义的,要正常表示一个\,需要两个\来表示
>>> path = "c:\\a\\b\\c" #字符串中的 \
>>> print(path)
c:\a\b\c
>>> re.match("c:\\\\a\\\\b\\\\c", path) #四个\。正则转一次,字符转转一次
<re.Match object; span=(0, 8), match='c:\\a\\b\\c'>
#第一个\转义第二个\,第三个\转义第四个\。然后两个\\字符串再转一次
推荐使用:re.match(r"")
r的作用:只关心正则的转义,并不关系字符串的转义
>>> re.match(r"c:\\a\\b\\c", path)
<re.Match object; span=(0, 8), match='c:\\a\\b\\c'>
建议以后在写正则的时候,一定要在正则表达式前面加上r
五、重复匹配(多位匹配)
代码 | 说明 |
---|---|
* | 匹配任意位(0~∞) |
+ | 至少一位(1~∞) |
? | ==0位==或者1位 |
{n} | 有n位 |
{n,} | 至少n位 |
{n,m} | n~m位 |
>>> s = "啊10环,你比9环多1环"
>>> re.findall(r"\d", s)
['1', '0', '9', '1']
# *
>>> re.match(".*", s)
<re.Match object; span=(0, 12), match='啊10环,你比9环多1环'>
# +
>>> re.findall(r"\d+", s)
['10', '9', '1'] #对比['1', '0', '9', '1']
# {} 指定位数
#以1开头,第二位是[34566789]其中一个,后面还有9位
>>> re.findall(r"1[356789]\d{9}", "1334567890123456789")
['13345678901']
#至少9位
>>> re.findall(r"1[356789]\d{9,}", "1334567890123456789")
['1334567890123456789']
r"</?.*?>" 匹配HTML标签 第一个?/?表示有/或者没有/ 第二个?是非贪婪模式
六、分组
在正则表达式中,使用圆括号 () 将表达式包裹起来,会形成正则匹配后的二次筛选。
>>> res = re.match(r".*?(\d+).*", s)
>>> res
<re.Match object; span=(0, 12), match='啊10环,你比9环多1环'>
>>> res.group()
'啊10环,你比9环多1环'
>>> res.group(1) #原正则中,只有一个()
'10' #对(\d+)进行二次匹配
>>> re.findall(r".*?(\d+).*", s)
['10'] #直接是二次筛选后的结果
>>> re.match(r"<\w+>.*</\w+>", "<a>连接地址</a>")
<re.Match object; span=(0, 11), match='<a>连接地址</a>'>
>>> re.match(r"<\w+>.*</\w+>", "<a>连接地址</b>")
<re.Match object; span=(0, 11), match='<a>连接地址</b>'>
#匹配<a></b>是有问题的
>>> re.match("<(\w+)>.*</\\1>", "<a>连接地址</a>")
<re.Match object; span=(0, 11), match='<a>连接地址</a>'>
>>> re.match(r"<(\w+)>.*</\1>", "<a>连接地址</a>")
<re.Match object; span=(0, 11), match='<a>连接地址</a>'>
>>> re.match(r"<(\w+)>.*</\1>", "<a>连接地址</b>")
>>>#没有匹配,返回None
re.match(r"<(\w+)>.*</\1>", “连接地址”) \1代表了第一个括号中的值
七、贪婪模式与非贪婪模式(懒惰模式)
Python,默认使用贪婪模式。尽可能匹配更多的字符
>>> ss
'\n<div>\n这是内容\n</div>\n<div>\n这是内容\n</div>'
>>> ss = re.sub(r"\n", "", ss)
>>> ss
'<div>这是内容</div><div>这是内容</div>'
>>> re.findall(r"<\w+>(.*)</\w+>", ss)
['这是内容</div><div>这是内容'] #贪婪模式
>>> re.findall(r"<\w+>(.*?)</\w+>", ss)
['这是内容', '这是内容'] #非贪婪模式
在 *、?、+、{n,m} 后面加上 ? ,可以将贪婪模式变为非贪婪模式。
代码 | 说明 |
---|---|
*? | 重复任意次,但是尽可能少重复 |
+? | 重复1次或者多次,但是尽可能少重复 |
?? | 重复0次或者1次,但是尽可能少重复 |
{n,m}? | 重复n到m次,但是尽可能少重复 |
{n,}? | 重复n次以上,但是尽可能少重复 |